/*
     This file is part of anastasis-gtk.
     Copyright (C) 2025 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_cells.c
 * @brief Widgets rendering the cells of our GtkColumnViews
 * @author Christian Grothoff
 */
#include <gnunet/gnunet_util_lib.h>
#include "anastasis_gtk_util.h"
#include "anastasis-gtk_helper.h"
#include "anastasis-gtk_rows.h"


/**
 * Padding above and below the rows of the two progress lists.
 */
#define PROGRESS_PADDING 15


/**
 * Return the row the cell of @a item shows and remember it on the
 * widget of that cell, so that a right-click can find it again.
 *
 * @param item the cell being bound
 * @return NULL if @a item shows no row
 */
static AGRow *
bind_row (GtkListItem *item)
{
  AGRow *row = AG_row_of (gtk_list_item_get_item (item));
  GtkWidget *child = gtk_list_item_get_child (item);

  if (NULL != child)
    AG_cell_set_row (child,
                     row);
  return row;
}


/**
 * Set the label of the cell of @a item to the @a field of its row.
 *
 * @param item the cell being bound
 * @param field field of the row to show
 */
static void
bind_text (GtkListItem *item,
           const char *field)
{
  AGRow *row = bind_row (item);
  const char *val = NULL;

  if (NULL != row)
    val = AG_row_string (row,
                         field);
  gtk_label_set_text (GTK_LABEL (gtk_list_item_get_child (item)),
                      (NULL != val) ? val : "");
}


/**
 * Define the "bind" handler @a name of a column showing the string
 * @a field of the rows.  The columns of our lists differ only in the
 * field they show, but the UI files must name a distinct handler for
 * each of them.
 *
 * @param name name of the handler to define
 * @param field field of the row the column shows
 */
#define TEXT_CELL(name, field)          \
  void                                  \
  name (GtkSignalListItemFactory *f,    \
        GtkListItem *item,              \
        gpointer user_data)             \
  {                                     \
    (void) f;                           \
    (void) user_data;                   \
    bind_text (item,                    \
               field);                  \
  }


/**
 * Create the label rendering a cell of a text column.
 *
 * @param f the factory of the column
 * @param item the cell to fill
 * @param user_data the builder
 */
void
AG_cell_setup_text (GtkSignalListItemFactory *f,
                    GtkListItem *item,
                    gpointer user_data)
{
  GtkWidget *label;

  (void) f;
  (void) user_data;
  label = gtk_label_new (NULL);
  gtk_label_set_xalign (GTK_LABEL (label),
                        0.0);
  gtk_widget_set_halign (label,
                         GTK_ALIGN_START);
  gtk_list_item_set_child (item,
                           label);
}


/**
 * Forget the row a cell showed once it is no longer shown.
 *
 * @param f the factory of the column
 * @param item the cell that was unbound
 * @param user_data the builder
 */
void
AG_cell_unbind (GtkSignalListItemFactory *f,
                GtkListItem *item,
                gpointer user_data)
{
  GtkWidget *child = gtk_list_item_get_child (item);

  (void) f;
  (void) user_data;
  if (NULL != child)
    AG_cell_set_row (child,
                     NULL);
}


/* ****************** progress lists ******************* */


/**
 * Create the label rendering a step of one of the progress lists.
 *
 * @param f the factory of the column
 * @param item the cell to fill
 * @param user_data the builder
 */
void
AG_cell_setup_progress (GtkSignalListItemFactory *f,
                        GtkListItem *item,
                        gpointer user_data)
{
  GtkWidget *label;

  AG_cell_setup_text (f,
                      item,
                      user_data);
  label = gtk_list_item_get_child (item);
  gtk_widget_set_margin_top (label,
                             PROGRESS_PADDING);
  gtk_widget_set_margin_bottom (label,
                                PROGRESS_PADDING);
}


/**
 * Show the description of a step of one of the progress lists, using the
 * long explanation as the tooltip.
 *
 * @param f the factory of the column
 * @param item the cell to fill
 * @param user_data the builder
 */
void
AG_cell_bind_progress (GtkSignalListItemFactory *f,
                       GtkListItem *item,
                       gpointer user_data)
{
  AGRow *row;

  (void) f;
  (void) user_data;
  bind_text (item,
             "description");
  row = AG_row_of (gtk_list_item_get_item (item));
  if (NULL == row)
    return;
  gtk_widget_set_tooltip_text (gtk_list_item_get_child (item),
                               AG_row_string (row,
                                              "tooltip"));
}


/* ************ continents, countries, methods ************* */


TEXT_CELL (AG_cell_bind_continent, "name_i18n")
TEXT_CELL (AG_cell_bind_country, "name")
TEXT_CELL (AG_cell_bind_auth_type, "type")
TEXT_CELL (AG_cell_bind_auth_details, "visualization")


/* ******************* policy review ******************** */


/**
 * Create the expander and label rendering the first column of the policy
 * review, where the challenges are nested under their policy.
 *
 * @param f the factory of the column
 * @param item the cell to fill
 * @param user_data the builder
 */
void
AG_cell_setup_policy_name (GtkSignalListItemFactory *f,
                           GtkListItem *item,
                           gpointer user_data)
{
  GtkWidget *expander;
  GtkWidget *label;

  (void) f;
  (void) user_data;
  label = gtk_label_new (NULL);
  gtk_label_set_xalign (GTK_LABEL (label),
                        0.0);
  gtk_widget_set_halign (label,
                         GTK_ALIGN_START);
  expander = gtk_tree_expander_new ();
  gtk_tree_expander_set_child (GTK_TREE_EXPANDER (expander),
                               label);
  gtk_list_item_set_child (item,
                           expander);
}


/**
 * Show the name of a policy, or the instructions of a challenge.
 *
 * @param f the factory of the column
 * @param item the cell to fill
 * @param user_data the builder
 */
void
AG_cell_bind_policy_name (GtkSignalListItemFactory *f,
                          GtkListItem *item,
                          gpointer user_data)
{
  GtkTreeExpander *expander;
  GtkWidget *label;
  AGRow *row;
  const char *name = NULL;

  (void) f;
  (void) user_data;
  expander = GTK_TREE_EXPANDER (gtk_list_item_get_child (item));
  gtk_tree_expander_set_list_row (expander,
                                  GTK_TREE_LIST_ROW (
                                    gtk_list_item_get_item (item)));
  row = bind_row (item);
  if (NULL != row)
    name = AG_row_string (row,
                          "policy_name");
  label = gtk_tree_expander_get_child (expander);
  gtk_label_set_text (GTK_LABEL (label),
                      (NULL != name) ? name : "");
}


TEXT_CELL (AG_cell_bind_policy_type, "method_type")
TEXT_CELL (AG_cell_bind_policy_cost, "cost")
TEXT_CELL (AG_cell_bind_policy_provider, "provider_name")


/* ****************** payment QR codes ******************* */


/**
 * Create the picture rendering a QR code.
 *
 * @param f the factory of the column
 * @param item the cell to fill
 * @param user_data the builder
 */
void
AG_cell_setup_qr_image (GtkSignalListItemFactory *f,
                        GtkListItem *item,
                        gpointer user_data)
{
  GtkWidget *picture;

  (void) f;
  (void) user_data;
  picture = gtk_picture_new ();
  /* render the QR code at its natural size */
  gtk_picture_set_can_shrink (GTK_PICTURE (picture),
                              FALSE);
  gtk_picture_set_content_fit (GTK_PICTURE (picture),
                               GTK_CONTENT_FIT_SCALE_DOWN);
  /* a GtkCellRendererPixbuf centred its image in the cell */
  gtk_widget_set_halign (picture,
                         GTK_ALIGN_CENTER);
  gtk_list_item_set_child (item,
                           picture);
}


/**
 * Show the QR code of a payment request.
 *
 * @param f the factory of the column
 * @param item the cell to fill
 * @param user_data the builder
 */
void
AG_cell_bind_qr_image (GtkSignalListItemFactory *f,
                       GtkListItem *item,
                       gpointer user_data)
{
  AGRow *row = bind_row (item);

  (void) f;
  (void) user_data;
  gtk_picture_set_paintable (GTK_PICTURE (gtk_list_item_get_child (item)),
                             (NULL != row)
                             ? AG_row_image (row)
                             : NULL);
}


TEXT_CELL (AG_cell_bind_qr_uri, "url")


/* ***************** completed backup ******************* */


TEXT_CELL (AG_cell_bind_backup_provider, "provider_name")


/**
 * Show the version of the policy stored at a provider, but only for the
 * providers the backup actually reached.
 *
 * @param f the factory of the column
 * @param item the cell to fill
 * @param user_data the builder
 */
void
AG_cell_bind_backup_version (GtkSignalListItemFactory *f,
                             GtkListItem *item,
                             gpointer user_data)
{
  AGRow *row = bind_row (item);
  GtkWidget *label = gtk_list_item_get_child (item);
  char *version;

  (void) f;
  (void) user_data;
  if (NULL == row)
  {
    gtk_widget_set_visible (label,
                            FALSE);
    return;
  }
  GNUNET_asprintf (&version,
                   "%llu",
                   (unsigned long long) AG_row_uint (row,
                                                     "version"));
  gtk_label_set_text (GTK_LABEL (label),
                      version);
  GNUNET_free (version);
  gtk_widget_set_visible (label,
                          AG_row_bool (row,
                                       "success"));
}


/* ***************** secret selection ******************* */


TEXT_CELL (AG_cell_bind_secret_name, "secret_name")
TEXT_CELL (AG_cell_bind_secret_time, "date_string")


/* ****************** provider list ******************** */


/**
 * The user toggled whether a provider is to be used.
 *
 * @param button the check button of the row
 * @param user_data NULL
 */
static void
provider_use_toggled_cb (GtkCheckButton *button,
                         gpointer user_data)
{
  AGRow *row;
  bool active;

  (void) user_data;
  row = AG_cell_get_row (GTK_WIDGET (button));
  if (NULL == row)
    return; /* not bound to a row (yet) */
  active = gtk_check_button_get_active (button);
  if (active == AG_row_bool (row,
                             "enabled"))
    return; /* we set the state ourselves while binding */
  AG_row_set (row,
              "enabled",
              json_boolean (active));
}


/**
 * Create the check button with which a provider is enabled or disabled.
 *
 * @param f the factory of the column
 * @param item the cell to fill
 * @param user_data the builder
 */
void
AG_cell_setup_provider_use (GtkSignalListItemFactory *f,
                            GtkListItem *item,
                            gpointer user_data)
{
  GtkWidget *check;

  (void) f;
  (void) user_data;
  check = gtk_check_button_new ();
  gtk_widget_set_halign (check,
                         GTK_ALIGN_CENTER);
  g_signal_connect (check,
                    "toggled",
                    G_CALLBACK (&provider_use_toggled_cb),
                    NULL);
  gtk_list_item_set_child (item,
                           check);
}


/**
 * Show whether a provider is to be used.  Providers we could not talk to
 * cannot be enabled, which is shown as an indeterminate state.
 *
 * @param f the factory of the column
 * @param item the cell to fill
 * @param user_data the builder
 */
void
AG_cell_bind_provider_use (GtkSignalListItemFactory *f,
                           GtkListItem *item,
                           gpointer user_data)
{
  GtkCheckButton *check;
  AGRow *row;

  (void) f;
  (void) user_data;
  /* the row must be known before we change the state, or the "toggled"
     handler cannot tell our own change from the user's */
  row = bind_row (item);
  check = GTK_CHECK_BUTTON (gtk_list_item_get_child (item));
  if (NULL == row)
    return;
  gtk_check_button_set_active (check,
                               AG_row_bool (row,
                                            "enabled"));
  gtk_check_button_set_inconsistent (check,
                                     ! AG_row_bool (row,
                                                    "sensitive"));
  gtk_widget_set_sensitive (GTK_WIDGET (check),
                            AG_row_bool (row,
                                         "sensitive"));
}


TEXT_CELL (AG_cell_bind_provider_name, "name")


/**
 * Show the liability limit of a provider, which we only know for the
 * providers we could talk to.
 *
 * @param f the factory of the column
 * @param item the cell to fill
 * @param user_data the builder
 */
void
AG_cell_bind_provider_liability (GtkSignalListItemFactory *f,
                                 GtkListItem *item,
                                 gpointer user_data)
{
  AGRow *row;

  (void) f;
  (void) user_data;
  bind_text (item,
             "liability");
  row = AG_row_of (gtk_list_item_get_item (item));
  gtk_widget_set_visible (gtk_list_item_get_child (item),
                          (NULL != row) &&
                          AG_row_bool (row,
                                       "sensitive"));
}


/**
 * Show the status of a provider in the colour that goes with it.
 *
 * @param f the factory of the column
 * @param item the cell to fill
 * @param user_data the builder
 */
void
AG_cell_bind_provider_status (GtkSignalListItemFactory *f,
                              GtkListItem *item,
                              gpointer user_data)
{
  AGRow *row = bind_row (item);
  GtkLabel *label = GTK_LABEL (gtk_list_item_get_child (item));
  const char *status;
  const char *color;

  (void) f;
  (void) user_data;
  status = (NULL != row)
           ? AG_row_string (row,
                            "status")
           : NULL;
  if (NULL == status)
  {
    gtk_label_set_text (label,
                        "");
    return;
  }
  color = AG_row_string (row,
                         "color");
  if (NULL == color)
  {
    gtk_label_set_text (label,
                        status);
    return;
  }
  {
    char *markup;

    markup = g_markup_printf_escaped ("<span foreground=\"%s\">%s</span>",
                                      color,
                                      status);
    gtk_label_set_markup (label,
                          markup);
    g_free (markup);
  }
}


/* end of anastasis-gtk_cells.c */
