/*
     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_rows.h
 * @brief Rows of the GtkColumnViews and the models holding them
 * @author Christian Grothoff
 */
#ifndef ANASTASIS_GTK_ROWS_H
#define ANASTASIS_GTK_ROWS_H

#include <gtk/gtk.h>
#include <jansson.h>


/**
 * One row of one of our lists.  The values shown in the cells and the
 * bookkeeping data that goes with them are kept as JSON, which is also
 * what the reducer state we build the rows from is made of.
 */
#define AG_TYPE_ROW (AG_row_get_type ())

G_DECLARE_FINAL_TYPE (AGRow,
                      AG_row,
                      AG,
                      ROW,
                      GObject);


/**
 * Create a row with the given @a fields.
 *
 * @param fields the values of the row, ownership is taken
 * @return the new row
 */
AGRow *
AG_row_new (json_t *fields);


/**
 * Change the @a field of @a row to @a value.
 *
 * @param row the row to update
 * @param field name of the field
 * @param value new value, ownership is taken
 */
void
AG_row_set (AGRow *row,
            const char *field,
            json_t *value);


/**
 * Obtain the string @a field of @a row.
 *
 * @param row the row to inspect
 * @param field name of the field
 * @return NULL if there is no such string field
 */
const char *
AG_row_string (AGRow *row,
               const char *field);


/**
 * Obtain the boolean @a field of @a row.
 *
 * @param row the row to inspect
 * @param field name of the field
 * @return false if there is no such field
 */
bool
AG_row_bool (AGRow *row,
             const char *field);


/**
 * Obtain the numeric @a field of @a row.
 *
 * @param row the row to inspect
 * @param field name of the field
 * @return 0 if there is no such field
 */
uint64_t
AG_row_uint (AGRow *row,
             const char *field);


/**
 * Obtain the @a field of @a row as JSON.
 *
 * @param row the row to inspect
 * @param field name of the field
 * @return NULL if there is no such field, otherwise
 *         a value borrowed from @a row
 */
json_t *
AG_row_json (AGRow *row,
             const char *field);


/**
 * Set the image @a row is to be rendered with.
 *
 * @param row the row to update
 * @param image the image, ownership is taken; may be NULL
 */
void
AG_row_set_image (AGRow *row,
                  GdkPaintable *image);


/**
 * Return the image @a row is rendered with.
 *
 * @param row the row to inspect
 * @return NULL if @a row has no image
 */
GdkPaintable *
AG_row_image (AGRow *row);


/**
 * Return the rows nested under @a row, creating the list on first use.
 * Only rows of a tree ever have children.
 *
 * @param row the parent row
 * @return the children of @a row, owned by @a row
 */
GListStore *
AG_row_children (AGRow *row);


/* ***************** the lists of rows ****************** */


/**
 * Return the list of rows called @a name in the main window.
 *
 * @param name name of the model in the UI file
 * @return the model, NULL on error
 */
GListStore *
AG_list (const char *name);


/**
 * Return the list of rows called @a name in the window @a builder built.
 *
 * @param builder builder of the window holding the list
 * @param name name of the model in the UI file
 * @return the model, NULL on error
 */
GListStore *
AG_list_of (GtkBuilder *builder,
            const char *name);


/**
 * Remove all rows from the list called @a name in the main window.
 *
 * @param name name of the model in the UI file
 */
void
AG_list_clear (const char *name);


/**
 * Append a row with the given @a fields to @a list.
 *
 * @param list list to append to
 * @param fields the values of the row, ownership is taken
 * @return the new row, owned by @a list
 */
AGRow *
AG_list_append (GListStore *list,
                json_t *fields);


/**
 * Append @a row to @a list.  Rows that have children must be filled
 * before they are added, as only then does the list learn that they can
 * be expanded.
 *
 * @param list list to append to
 * @param row the row to append, ownership is taken
 */
void
AG_list_add_row (GListStore *list,
                 AGRow *row);


/**
 * Append a row with the given @a fields to the list called @a name in
 * the main window.
 *
 * @param name name of the model in the UI file
 * @param fields the values of the row, ownership is taken
 * @return the new row, owned by the list
 */
AGRow *
AG_list_add (const char *name,
             json_t *fields);


/**
 * Return the row @a item stands for.  Rows of a tree are wrapped into a
 * `GtkTreeListRow` by the model, this unwraps them.
 *
 * @param item item of a list model, may be NULL
 * @return NULL if @a item is NULL
 */
AGRow *
AG_row_of (gpointer item);


/**
 * Attach the row a cell shows to the @a widget rendering it, so that
 * #AG_row_at() can find the row a click landed on.
 *
 * @param widget widget rendering the cell
 * @param row the row being rendered, may be NULL
 */
void
AG_cell_set_row (GtkWidget *widget,
                 AGRow *row);


/**
 * Return the row the cell rendered by @a widget shows.
 *
 * @param widget widget rendering a cell
 * @return NULL if @a widget is not bound to a row
 */
AGRow *
AG_cell_get_row (GtkWidget *widget);


/* ******************* selections ********************* */


/**
 * Return the row that is selected in @a selection.
 *
 * @param selection the selection to inspect
 * @return NULL if nothing is selected
 */
AGRow *
AG_selected (GtkSingleSelection *selection);


/**
 * Return the row selected in the selection called @a name of the main
 * window.
 *
 * @param name name of the selection in the UI file
 * @return NULL if nothing is selected
 */
AGRow *
AG_selected_row (const char *name);


/**
 * Select the row at @a position in the selection called @a name of the
 * main window.
 *
 * @param name name of the selection in the UI file
 * @param position index of the row to select
 */
void
AG_select_row (const char *name,
               guint position);


/**
 * Return the row shown at @a y in @a view.  Used to find the row a
 * context menu was opened on.
 *
 * @param view the column view that was clicked
 * @param y vertical position of the click, in widget coordinates
 * @return NULL if there is no row at @a y
 */
AGRow *
AG_row_at (GtkWidget *view,
           double y);


/* ****************** sorting ******************* */


/**
 * How a column of a list is to be sorted.
 */
struct AG_SortSpec
{
  /**
   * Name of the `GtkColumnViewColumn` in the UI file.
   */
  const char *column;

  /**
   * Field of the rows to compare.
   */
  const char *field;

  /**
   * True to compare the field numerically instead of as text.
   */
  bool numeric;
};


/**
 * Make the columns given in @a specs of the column view called @a view
 * sort the rows of @a sort_model.  Both objects are looked up in @a
 * builder.
 *
 * @param builder builder of the window holding the list
 * @param view name of the `GtkColumnView` in the UI file
 * @param sort_model name of the `GtkSortListModel` feeding @a view
 * @param specs the columns to make sortable, terminated by a NULL column
 */
void
AG_list_make_sortable (GtkBuilder *builder,
                       const char *view,
                       const char *sort_model,
                       const struct AG_SortSpec *specs);


/**
 * Set up those models of the main window that cannot be described in
 * the UI file.  To be called once, after the main window was built.
 */
void
AG_lists_setup (void);


#endif
