/*
     This file is part of anastasis-gtk.
     Copyright (C) 2020, 2021, 2022 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.c
 * @brief Main function of anastasis-gtk
 * @author Christian Grothoff
 * @author Dennis Neufeld
 */
#include <gnunet/gnunet_util_lib.h>
#include "anastasis_gtk_util.h"
#include "anastasis-gtk_action.h"
#include "anastasis-gtk_helper.h"
#include "anastasis-gtk_progress.h"
#include "anastasis-gtk_rows.h"
#include <jansson.h>

/**
 * Handle to our main loop.
 */
struct ANASTASIS_GTK_MainLoop *AG_ml;

/**
 * Active policy discovery job, or NULL.
 */
struct ANASTASIS_PolicyDiscovery *AG_pd;

/**
 * Our configuration.
 */
const struct GNUNET_CONFIGURATION_Handle *AG_cfg;

/**
 * Application ID to include in the user attributes.
 * (-a option).
 */
char *AG_application_id;

/**
 * Closure for #GNUNET_CURL_gnunet_scheduler_reschedule().
 */
static struct GNUNET_CURL_RescheduleContext *rc;

/**
 * Hash map from UUID hashes to GtkWidgets.
 */
struct GNUNET_CONTAINER_MultiHashMap *AG_entry_attributes;

/**
 * Curl context for communication with taler backend
 */
struct GNUNET_CURL_Context *AG_ctx;

/**
 * Handle to an ongoing action.
 */
struct ANASTASIS_ReduxAction *AG_ra;

/**
 * Handle to an ongoing background action.
 */
struct ANASTASIS_LongAction AG_lacs[ANASTASIS_LP_CNT];


/**
 * Actual state.
 */
json_t *AG_redux_state;


void
AG_stop_long_action (void)
{
  for (enum ANASTASIS_LongActionKind i = 0;
       i < ANASTASIS_LP_CNT;
       i++)
  {
    struct ANASTASIS_LongAction *la = &AG_lacs[i];

    if (NULL != la->ra)
    {
      ANASTASIS_redux_action_cancel (la->ra);
      la->ra = NULL;
    }
    if (NULL != la->task)
    {
      GNUNET_SCHEDULER_cancel (la->task);
      la->task = NULL;
    }
  }
}


/**
 * Task run on shutdown.
 *
 * @param cls unused
 */
static void
shutdown_task (void *cls)
{
  (void) cls;
  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
              "Shutdown initiated\n");
  if (NULL != AG_pd)
  {
    ANASTASIS_policy_discovery_stop (AG_pd);
    AG_pd = NULL;
  }
  ANASTASIS_redux_done ();
  if (NULL != AG_ra)
  {
    ANASTASIS_redux_action_cancel (AG_ra);
    AG_ra = NULL;
  }
  AG_stop_long_action ();
  if (NULL != AG_ctx)
  {
    GNUNET_CURL_fini (AG_ctx);
    AG_ctx = NULL;
  }
  if (NULL != rc)
  {
    GNUNET_CURL_gnunet_rc_destroy (rc);
    rc = NULL;
  }
  ANASTASIS_GTK_main_loop_quit (AG_ml);
  AG_ml = NULL;
  GNUNET_CONTAINER_multihashmap_destroy (AG_entry_attributes);
  AG_entry_attributes = NULL;
  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
              "Shutdown complete\n");
}


/**
 * Callback invoked if the application is supposed to exit, i.e. the user
 * closed the main window.
 *
 * @param window the main window
 * @param user_data unused
 * @return TRUE, as our own shutdown sequence takes the window down
 */
gboolean
anastasis_gtk_quit_cb (GtkWindow *window,
                       gpointer user_data)
{
  (void) window;
  (void) user_data;
  GNUNET_SCHEDULER_shutdown ();
  return TRUE;
}


/**
 * User clicked the "quit" button.
 *
 * @param button the button
 * @param user_data unused
 */
void
anastasis_gtk_main_window_quit_button_clicked_cb (GtkButton *button,
                                                  gpointer user_data)
{
  GNUNET_SCHEDULER_shutdown ();
}


void
AG_load (const char *filename)
{
  json_error_t error;
  json_t *in;

  in = json_load_file (filename,
                       JSON_REJECT_DUPLICATES,
                       &error);
  if (NULL == in)
  {
    AG_error ("Failed to parse file `%s' at %d:%d: %s\n",
              filename,
              error.line,
              error.column,
              error.text);
    return;
  }
  AG_action_cb (NULL,
                TALER_EC_NONE,
                in);
  json_decref (in);
}


/**
 * Actual main function run right after GNUnet's scheduler
 * is initialized.  Initializes GTK and builds the main window.
 *
 * @param cls NULL
 */
static void
run (void *cls)
{
  GtkWidget *main_window;
  int argc;
  char *const *argv;

  AG_ml = cls;
  AG_entry_attributes = GNUNET_CONTAINER_multihashmap_create (16,
                                                              GNUNET_NO);
  ANASTASIS_GTK_setup_nls ();
  /* the UI files name AGRow as the item type of their models, so the type
     has to be registered before GtkBuilder parses them */
  g_type_ensure (AG_TYPE_ROW);
  if (GNUNET_OK !=
      ANASTASIS_GTK_main_loop_build_window (ANASTASIS_GTK_project_data (),
                                         AG_ml,
                                         NULL))
    return;
  AG_lists_setup ();
  AG_progress_setup ();
  AG_cfg = ANASTASIS_GTK_main_loop_get_gtk_configuration (AG_ml);
  ANASTASIS_GTK_main_loop_get_args (AG_ml,
                                 &argc,
                                 &argv);
  /* setup main window */
  main_window = GTK_WIDGET (
    GCG_get_main_window_object ("anastasis_gtk_main_window"));
  gtk_window_maximize (GTK_WINDOW (main_window));
  AG_insensitive ("anastasis_gtk_main_window_forward_button");
  /* make GUI visible */
  gtk_window_present (GTK_WINDOW (main_window));
  GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
                                 NULL);
  /* initialize HTTP client */
  AG_ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
                             &rc);
  rc = GNUNET_CURL_gnunet_rc_create (AG_ctx);
  ANASTASIS_redux_init (AG_ctx);
  if (0 != argc)
    AG_load (argv[0]);
}


/**
 * Main function of anastasis-gtk.
 *
 * @param argc number of arguments
 * @param argv arguments
 * @return 0 on success
 */
int
main (int argc,
      char *const *argv)
{
  struct GNUNET_GETOPT_CommandLineOption options[] = {
    GNUNET_GETOPT_option_string ('A',
                                 "application",
                                 "ID",
                                 "set the application ID",
                                 &AG_application_id),
    GNUNET_GETOPT_OPTION_END
  };
  int ret;

  AG_application_id = GNUNET_strdup ("anastasis-standalone");
  if (GNUNET_OK !=
      ANASTASIS_GTK_main_loop_start (ANASTASIS_GTK_project_data (),
                                  "anastasis-gtk",
                                  "GTK GUI for Anastasis",
                                  argc,
                                  argv,
                                  options,
                                  "anastasis_gtk_main_window.ui",
                                  &run))
    ret = 1;
  else
    ret = 0;
  GNUNET_free (AG_application_id);
  return ret;
}


/* end of anastasis-gtk.c */
