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


/**
 * Lookup autocompletion string for @a editable widget.
 *
 * @param editable widget to lookup
 * @return NULL if not found
 */
static const char *
lookup_autocomplete (GtkEditable *editable)
{
  json_t *a;
  size_t index;
  json_t *ra = json_object_get (AG_redux_state,
                                "required_attributes");
  if (NULL == ra)
  {
    GNUNET_break (0);
    return NULL;
  }
  json_array_foreach (ra, index, a)
  {
    const char *autocomplete = NULL;
    const char *widget_name = NULL;
    const char *type = NULL;
    char *data_name;
    bool match;
    struct GNUNET_JSON_Specification spec[] = {
      GNUNET_JSON_spec_mark_optional (
        GNUNET_JSON_spec_string ("widget",
                                 &widget_name),
        NULL),
      GNUNET_JSON_spec_mark_optional (
        GNUNET_JSON_spec_string ("autocomplete",
                                 &autocomplete),
        NULL),
      GNUNET_JSON_spec_string ("type",
                               &type),
      GNUNET_JSON_spec_end ()
    };

    if (GNUNET_OK !=
        GNUNET_JSON_parse (a,
                           spec,
                           NULL, NULL))
    {
      GNUNET_break (0);
      continue;
    }
    if ( (NULL == autocomplete) ||
         (NULL == widget_name) )
      continue;
    data_name = AG_expand_name (widget_name,
                                type);
    match = (editable ==
             GTK_EDITABLE (GCG_get_main_window_object (data_name)));
    GNUNET_free (data_name);
    if (match)
      return autocomplete;
  }
  GNUNET_break (0);
  return NULL;
}


/**
 * Function called from an auto-completed entry widget up on upon text
 * deletion.
 *
 * @param editable the widget
 * @param start_pos starting position
 * @param end_pos end position
 * @param user_data unused
 */
void
anastasis_gtk_autocomplete_delete_text (GtkEditable *editable,
                                        int start_pos,
                                        int end_pos,
                                        void *user_data)
{
  const char *ac;
  (void) user_data;

  ac = lookup_autocomplete (editable);
  if (NULL == ac)
    return;
  if (0 == start_pos)
    return;
#if CONFUSING_UX_DESIRED
  if (ac[start_pos] != '?')
  {
    g_signal_stop_emission_by_name (editable,
                                    "delete-text");
    gtk_editable_delete_text (editable,
                              start_pos - 1,
                              end_pos);
  }
#endif
}


/**
 * Function called from an auto-completed widget upon text insertion.
 *
 * @param editable the widget
 * @param new_text inserted text
 * @param new_text_length number of bytes in @a new_text
 * @param position insertion position
 * @param user_data unused
 */
void
anastasis_gtk_autocomplete_insert_text (GtkEditable *editable,
                                        const char *new_text,
                                        int new_text_length,
                                        gpointer position,
                                        void *user_data)
{
  const char *ac;
  gint pos;
  char *replacement;

  (void) user_data;
  ac = lookup_autocomplete (editable);
  if (NULL == ac)
    return;
  pos = gtk_editable_get_position (editable);
  if (strlen (ac) <= pos + new_text_length)
    return;
  if (ac[pos + new_text_length] != '?')
  {
    g_signal_stop_emission_by_name (editable,
                                    "insert-text");
    GNUNET_asprintf (&replacement,
                     "%.*s%c",
                     new_text_length,
                     new_text,
                     ac[pos + new_text_length]);
    gtk_editable_insert_text (editable,
                              replacement,
                              -1,
                              position);
    GNUNET_free (replacement);
  }
}


/**
 * Function called from an auto-completed PIN widget upon text insertion.
 *
 * @param editable the widget
 * @param new_text inserted text
 * @param new_text_length number of bytes in @a new_text
 * @param position insertion position
 * @param user_data unused
 */
void
anastasis_gtk_pin_autocomplete_insert_text (GtkEditable *editable,
                                            const char *new_text,
                                            int new_text_length,
                                            gpointer position,
                                            void *user_data)
{
  /* Use \? to break up trigraphs */
  const char *acn =   "????\?-??\?-???\?-??\?";
  const char *aca = "A-????\?-??\?-???\?-??\?";
  const char *ac;
  gint pos;
  char *replacement;
  gchar *pfx;

  (void) user_data;
  pfx = gtk_editable_get_chars (editable,
                                0,
                                2);
  if (0 == strncasecmp (pfx,
                        "A-",
                        2))
    ac = aca;
  else
    ac = acn;
  g_free (pfx);

  pos = gtk_editable_get_position (editable);
  if (strlen (ac) <= pos + new_text_length)
    return;
  if (ac[pos + new_text_length] != '?')
  {
    g_signal_stop_emission_by_name (editable,
                                    "insert-text");
    GNUNET_asprintf (&replacement,
                     "%.*s%c",
                     new_text_length,
                     new_text,
                     ac[pos + new_text_length]);
    gtk_editable_insert_text (editable,
                              replacement,
                              -1,
                              position);
    GNUNET_free (replacement);
  }
}
