/*
 * This file is part of GNUnet
 * Copyright (C) 2009-2014 GNUnet e.V.
 *
 * GNUnet 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.
 *
 * GNUnet 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 GNUnet; see the file COPYING.  If not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */
/**
 * @file namestore/plugin_gtk_namestore_box.c
 * @brief namestore plugin for editing BOXed records
 * @author Christian Grothoff
 *
 * This code is a bit hackish, as we re-use the existing logic for the
 * boxed type.  This is done by switching on the record type of the
 * box -- and by including the code of the other plugins in this
 * code, and the widgets of the other plugins in the XML for the box
 * plugin.  This is particularly hackish as we then need to make sure
 * that the names of the shared widgets and callbacks are identical
 * across the different plugins that are being boxed.  This currently
 * affects the SRV and TLSA plugins.
 */
#include "gnunet_gtk.h"
#include "gnunet_gtk_namestore_plugin.h"
#if HAVE_GNUTLS
#include <gnutls/gnutls.h>
#endif

#ifndef EDP_CBC_DEF
#define EDP_CBC_DEF
/**
 * The user has changed the protocol selection.  Enable/disable 'save'
 * button depending on the validity of the value.
 *
 * @param entry editing widget
 * @param user_data the plugin environment
 */
static void
edit_dialog_protocol_combobox_changed_cb (GtkEditable *entry,
                                          gpointer user_data)
{
  struct GNUNET_GTK_NAMESTORE_PluginEnvironment *edc = user_data;

  edc->check_validity (edc);
}
#endif

/* We simply include the code for boxed plugins directly here */
#include "plugin_gtk_namestore_srv.c"
#if HAVE_GNUTLS
#include "plugin_gtk_namestore_tlsa.c"
#endif

/**
 * Context for the box.
 */
struct BoxContext
{
  /**
   * Our operating environment.
   */
  struct GNUNET_GTK_NAMESTORE_PluginEnvironment *env;

  /**
   * Record type in the box that we are editing.
   */
  unsigned int record_type;
};


/**
 * Function that will be called to initialize the builder's
 * widgets from the existing record (if there is one).
 * The `n_value` is the existing value of the record as a string.
 *
 * @param cls the `struct BoxContext *'
 * @param n_value the record as a string
 * @param builder the edit dialog's builder
 */
static void
box_load (void *cls, gchar *n_value, GtkBuilder *builder)
{
  struct BoxContext *bc = cls;
  unsigned int protocol;
  unsigned int service;
  unsigned int record_type;
  size_t slen = strlen (n_value) + 1;
  char rest[slen];

  if (4 !=
      sscanf (n_value, "%u %u %u %s", &protocol, &service, &record_type, rest))
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                _ ("Unable to parse (boxed) BOX record `%s'\n"),
                n_value);
    return;
  }
  bc->record_type = record_type;
  switch (record_type)
  {
  case GNUNET_DNSPARSER_TYPE_SRV:
    gtk_widget_show (
      GTK_WIDGET (gtk_builder_get_object (builder, "edit_dialog_srv_frame")));
    srv_load (bc->env, n_value, builder);
    break;
#if HAVE_GNUTLS
  case GNUNET_DNSPARSER_TYPE_TLSA:
    gtk_widget_show (
      GTK_WIDGET (gtk_builder_get_object (builder, "edit_dialog_tlsa_frame")));
    tlsa_load (bc->env, n_value, builder);
    break;
#endif
  default:
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                _ ("Boxed record type %u not supported\n"),
                record_type);
    break;
  }
}


/**
 * Function that will be called to retrieve the final value of the
 * record (in string format) once the dialog is being closed.
 *
 * @param cls the `struct BoxContext *'
 * @param builder the edit dialog's builder
 * @return record value as a string, as specified in the dialog
 */
static gchar *
box_store (void *cls, GtkBuilder *builder)
{
  struct BoxContext *bc = cls;

  switch (bc->record_type)
  {
  case GNUNET_DNSPARSER_TYPE_SRV:
    return srv_store (bc->env, builder);
#if HAVE_GNUTLS
  case GNUNET_DNSPARSER_TYPE_TLSA:
    return tlsa_store (bc->env, builder);
#endif
  default:
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                _ ("Boxed record type %u not supported\n"),
                bc->record_type);
    return NULL;
  }
}


/**
 * Function to call to validate the state of the dialog.  Should
 * return #GNUNET_OK if the information in the dialog is valid, and
 * #GNUNET_SYSERR if some fields contain invalid values.  The
 * function should highlight fields with invalid inputs for the
 * user.
 *
 * @param cls the `struct BoxContext *'
 * @param builder the edit dialog's builder
 * @return #GNUNET_OK if there is a valid record value in the dialog
 */
static int
box_validate (void *cls, GtkBuilder *builder)
{
  struct BoxContext *bc = cls;

  switch (bc->record_type)
  {
  case GNUNET_DNSPARSER_TYPE_SRV:
    return srv_validate (bc->env, builder);
#if HAVE_GNUTLS
  case GNUNET_DNSPARSER_TYPE_TLSA:
    return tlsa_validate (bc->env, builder);
#endif
  default:
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                _ ("Boxed record type %u not supported\n"),
                bc->record_type);
    return GNUNET_SYSERR;
  }
}


/**
 * Entry point for the plugin.
 *
 * @param cls the `struct GNUNET_GTK_NAMESTORE_PluginEnvironment`
 * @return NULL on error, otherwise the plugin context
 */
void *
libgnunet_plugin_gtk_namestore_box_init (void *cls)
{
  struct GNUNET_GTK_NAMESTORE_PluginEnvironment *env = cls;
  struct GNUNET_GTK_NAMESTORE_PluginFunctions *plugin;
  static struct GNUNET_GTK_NAMESTORE_Symbol symbols[] = {
    /* SRV plugin */
    {"GNS_edit_dialog_srv_target_entry_changed_cb",
     G_CALLBACK (GNS_edit_dialog_srv_target_entry_changed_cb)},
#if HAVE_GNUTLS
    /* TLSA plugin */
    {"tlsa_value_textbuffer_changed_cb",
     G_CALLBACK (tlsa_value_textbuffer_changed_cb)},
    {"edit_dialog_tlsa_selector_radiobutton_toggled_cb",
     G_CALLBACK (edit_dialog_tlsa_selector_radiobutton_toggled_cb)},
    {"edit_dialog_tlsa_usage_radiobutton_toggled_cb",
     G_CALLBACK (edit_dialog_tlsa_usage_radiobutton_toggled_cb)},
    {"edit_dialog_tlsa_matching_type_radiobutton_toggled_cb",
     G_CALLBACK (edit_dialog_tlsa_matching_type_radiobutton_toggled_cb)},
    {"tlsa_import_button_clicked_cb",
     G_CALLBACK (tlsa_import_button_clicked_cb)},
    {"edit_dialog_tlsa_import_entry_changed_cb",
     G_CALLBACK (edit_dialog_tlsa_import_entry_changed_cb)},
#endif
    /* generic CBs */
    {"edit_dialog_protocol_combobox_changed_cb",
     G_CALLBACK (edit_dialog_protocol_combobox_changed_cb)},
    /* need to include symbols from all boxed types here */
    {NULL, NULL}
  };
  struct BoxContext *bc;

#if HAVE_GNUTLS
  gnutls_global_init ();
#endif
  bc = GNUNET_new (struct BoxContext);
  bc->env = env;
  plugin = GNUNET_new (struct GNUNET_GTK_NAMESTORE_PluginFunctions);
  plugin->cls = bc;
  plugin->dialog_glade_filename = "gnunet_namestore_edit_box.glade";
  plugin->dialog_widget_name = "edit_box_dialog";
  plugin->symbols = symbols;
  plugin->load = &box_load;
  plugin->store = &box_store;
  plugin->validate = &box_validate;
  return plugin;
}


/**
 * Exit point from the plugin.
 *
 * @param cls the plugin context (as returned by "init")
 * @return always NULL
 */
void *
libgnunet_plugin_gtk_namestore_box_done (void *cls)
{
  struct GNUNET_GTK_NAMESTORE_PluginFunctions *plugin = cls;
  struct BoxContext *bc = plugin->cls;

  GNUNET_free (bc);
  GNUNET_free (plugin);
#if HAVE_GNUTLS
  gnutls_global_deinit ();
#endif
  return NULL;
}

/* end of plugin_gtk_namestore_box.c */
