/**
 * @file search-dlg.cc
 * @brief Part of GNOME Commander - A GNOME based file manager
 *
 * @copyright (C) 2006 Assaf Gordon\n
 * @copyright (C) 2007-2012 Piotr Eljasiak\n
 * @copyright (C) 2013-2023 Uwe Scholz\n
 *
 * @copyright This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * @copyright This program 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.
 *
 * @copyright You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include <config.h>
#include <string.h>

#include "libgviewer.h"
#include "gnome-cmd-includes.h"
#include "gnome-cmd-data.h"

using namespace std;


// HEX history doesn't work yet
#undef HEX_HISTORY

static GtkDialogClass *parent_class = nullptr;

void entry_changed(GtkEntry *entry, gpointer  user_data);
static void search_dlg_destroy (GtkObject *object);
static void search_dlg_action_response(GtkDialog *dlg, gint arg1, GViewerSearchDlg *sdlg);

struct GViewerSearchDlgPrivate
{
    GtkWidget  *table;
    GtkWidget  *label;
    GtkWidget  *entry;
    GtkWidget  *text_mode, *hex_mode;
    GtkWidget  *case_sensitive_checkbox;

    SEARCHMODE searchmode;

    gchar      *search_text_string;
    guint8     *search_hex_buffer;
    guint      search_hex_buflen;
};


guint8 *gviewer_search_dlg_get_search_hex_buffer (GViewerSearchDlg *sdlg, /*out*/ guint &buflen)
{
    g_return_val_if_fail (sdlg != nullptr, nullptr);
    g_return_val_if_fail (sdlg->priv != nullptr, nullptr);
    g_return_val_if_fail (sdlg->priv->search_hex_buffer != nullptr, nullptr);
    g_return_val_if_fail (sdlg->priv->search_hex_buflen>0, nullptr);

    guint8 *result = g_new0 (guint8, sdlg->priv->search_hex_buflen);
    memcpy (result, sdlg->priv->search_hex_buffer, sdlg->priv->search_hex_buflen);

    buflen = sdlg->priv->search_hex_buflen;

    return result;
}


gchar *gviewer_search_dlg_get_search_text_string (GViewerSearchDlg *sdlg)
{
    g_return_val_if_fail (sdlg != nullptr, nullptr);
    g_return_val_if_fail (sdlg->priv != nullptr, nullptr);
    g_return_val_if_fail (sdlg->priv->search_text_string != nullptr, nullptr);

    return g_strdup (sdlg->priv->search_text_string);
}


SEARCHMODE gviewer_search_dlg_get_search_mode (GViewerSearchDlg *sdlg)
{
    g_return_val_if_fail (sdlg != nullptr, SEARCH_MODE_TEXT);
    g_return_val_if_fail (sdlg->priv != nullptr, SEARCH_MODE_TEXT);

    return sdlg->priv->searchmode;
}


gboolean gviewer_search_dlg_get_case_sensitive (GViewerSearchDlg *sdlg)
{
    g_return_val_if_fail (sdlg != nullptr, TRUE);
    g_return_val_if_fail (sdlg->priv != nullptr, TRUE);

    return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sdlg->priv->case_sensitive_checkbox));
}


inline void set_text_history (GViewerSearchDlg *sdlg)
{
    for (GList *i=gnome_cmd_data.intviewer_defaults.text_patterns.ents; i; i=i->next)
        if (i->data)
            gtk_combo_box_append_text (GTK_COMBO_BOX (sdlg->priv->entry), (gchar *) i->data);
}


inline void set_text_mode (GViewerSearchDlg *sdlg)
{
    gtk_widget_grab_focus (sdlg->priv->entry);
    sdlg->priv->searchmode = SEARCH_MODE_TEXT;
    gtk_widget_set_sensitive(sdlg->priv->case_sensitive_checkbox, TRUE);

    GtkEntry *w = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (sdlg->priv->entry)));
    entry_changed (w, (gpointer) sdlg);
}


static void set_hex_mode (GViewerSearchDlg *sdlg)
{
#if defined(HEX_HISTORY)
    for (GList *i=gnome_cmd_data.intviewer_defaults.hex_patterns.ents; i; i=i->next)
        if (i->data)
            gtk_combo_box_prepend_text (GTK_COMBO_BOX (sdlg->priv->entry), (gchar *) i->data);
#endif
    gtk_widget_grab_focus (sdlg->priv->entry);

    sdlg->priv->searchmode = SEARCH_MODE_HEX;

    gtk_widget_set_sensitive(sdlg->priv->case_sensitive_checkbox, FALSE);

    // Check if the text in the GtkEntryBox has hex value, otherwise disable the "Find" button
    GtkEntry *w = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (sdlg->priv->entry)));
    entry_changed (w, (gpointer) sdlg);
}


static void search_mode_text (GtkToggleButton *btn, GViewerSearchDlg *sdlg)
{
    g_return_if_fail (btn != nullptr);
    g_return_if_fail (sdlg != nullptr);

    if (!gtk_toggle_button_get_active(btn))
        return;

    set_text_mode (sdlg);
}


static void search_mode_hex (GtkToggleButton *btn, GViewerSearchDlg *sdlg)
{
    g_return_if_fail (btn != nullptr);
    g_return_if_fail (sdlg != nullptr);

    if (gtk_toggle_button_get_active(btn))
        set_hex_mode(sdlg);
}


static void search_dlg_action_response (GtkDialog *dlg, gint arg1, GViewerSearchDlg *sdlg)
{
    g_return_if_fail (sdlg != nullptr);
    g_return_if_fail (sdlg->priv != nullptr);

    if (arg1 != GTK_RESPONSE_OK)
        return;

    g_return_if_fail (sdlg->priv->search_text_string==nullptr);
    g_return_if_fail (sdlg->priv->search_hex_buffer==nullptr);

    gchar *pattern = gtk_combo_box_get_active_text (GTK_COMBO_BOX (sdlg->priv->entry));

    sdlg->priv->search_text_string = g_strdup (pattern);

    if (sdlg->priv->searchmode==SEARCH_MODE_TEXT)   // text mode search
    {
        gnome_cmd_data.intviewer_defaults.text_patterns.add(pattern);

        gnome_cmd_data.intviewer_defaults.case_sensitive =
            gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sdlg->priv->case_sensitive_checkbox));
    }
    else    // hex mode search
    {
        sdlg->priv->search_hex_buffer = text2hex (pattern, sdlg->priv->search_hex_buflen);
        g_return_if_fail (sdlg->priv->search_hex_buffer != nullptr);

        gnome_cmd_data.intviewer_defaults.hex_patterns.add(pattern);
    }
}


static void class_init (GViewerSearchDlgClass *klass)
{
    GtkObjectClass *object_class = (GtkObjectClass *) klass;

    parent_class = (GtkDialogClass *) gtk_type_class (gtk_dialog_get_type ());
    object_class->destroy = search_dlg_destroy;
}


void entry_changed (GtkEntry *entry, gpointer  user_data)
{
    g_return_if_fail (IS_GVIEWER_SEARCH_DLG (user_data));

    gboolean enable = FALSE;

    GViewerSearchDlg *sdlg = GVIEWER_SEARCH_DLG (user_data);
    g_return_if_fail (sdlg->priv != nullptr);

    if (sdlg->priv->searchmode==SEARCH_MODE_HEX)
    {
        // Only if the use entered a valid hex string, enable the "find" button
        guint len;
        guint8 *buf = text2hex (gtk_entry_get_text (entry), len);

        enable = buf != nullptr && len>0;
        g_free (buf);
    }
    else
    {
        // SEARCH_MODE_TEXT
        enable = strlen (gtk_entry_get_text (entry))>0;
    }
    gtk_dialog_set_response_sensitive (GTK_DIALOG (user_data), GTK_RESPONSE_OK, enable);
}


static void search_dlg_init (GViewerSearchDlg *sdlg)
{
    GtkDialog *dlg = GTK_DIALOG (sdlg);

    GtkTable *table;
    GtkWidget *entry;

    sdlg->priv = g_new0 (GViewerSearchDlgPrivate, 1);

    sdlg->priv->searchmode = (SEARCHMODE) gnome_cmd_data.intviewer_defaults.search_mode;

    gtk_window_set_title (GTK_WINDOW (dlg), _("Find"));
    gtk_window_set_modal (GTK_WINDOW (dlg), TRUE);
    gtk_dialog_add_button (dlg, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
    gtk_dialog_add_button (dlg, GTK_STOCK_FIND, GTK_RESPONSE_OK);
    gtk_dialog_set_default_response (dlg, GTK_RESPONSE_OK);

    g_signal_connect_swapped (GTK_WIDGET (dlg), "response", G_CALLBACK (search_dlg_action_response), sdlg);

    // 2x4 Table
    table = GTK_TABLE (gtk_table_new (2, 2, FALSE));
    gtk_table_set_row_spacings (GTK_TABLE (table), 6);
    gtk_table_set_col_spacings (GTK_TABLE (table), 6);
    gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (dlg)), GTK_WIDGET (table), FALSE, TRUE, 0);
    sdlg->priv->table = GTK_WIDGET (table);

    // Label
    sdlg->priv->label = gtk_label_new(nullptr);
    gtk_label_set_markup_with_mnemonic (GTK_LABEL (sdlg->priv->label), "_Search for:");
    gtk_table_attach(table, sdlg->priv->label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0);

    // Entry Box
    sdlg->priv->entry = gtk_combo_box_entry_new_text();
    entry = gtk_bin_get_child (GTK_BIN (sdlg->priv->entry));
    g_object_set(entry, "activates-default", TRUE, nullptr);
    g_signal_connect (entry, "changed", G_CALLBACK (entry_changed), sdlg);
    gtk_table_attach(table, sdlg->priv->entry, 1, 3, 0, 1, GTK_FILL, GTK_FILL, 0, 0);

    set_text_history (sdlg);

    // Search mode radio buttons
    sdlg->priv->text_mode = gtk_radio_button_new_with_mnemonic(nullptr, _("_Text"));
    sdlg->priv->hex_mode = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON (sdlg->priv->text_mode), _("_Hexadecimal"));

    g_signal_connect (sdlg->priv->text_mode, "toggled", G_CALLBACK (search_mode_text), sdlg);
    g_signal_connect (sdlg->priv->hex_mode, "toggled", G_CALLBACK (search_mode_hex), sdlg);

    gtk_table_attach (table, sdlg->priv->text_mode, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
    gtk_table_attach (table, sdlg->priv->hex_mode, 1, 2, 2, 3, GTK_FILL, GTK_FILL, 0, 0);

    // Case-Sensitive Checkbox
    sdlg->priv->case_sensitive_checkbox = gtk_check_button_new_with_mnemonic(_("_Match case"));
    gtk_table_attach(table, sdlg->priv->case_sensitive_checkbox, 2, 3, 1, 2, GtkAttachOptions(GTK_EXPAND|GTK_FILL), GTK_FILL, 0, 0);

    gtk_widget_show_all(sdlg->priv->table);
    gtk_widget_show (GTK_WIDGET (dlg));

    // Restore the previously saved state (loaded with "load_search_dlg_state")
    if (sdlg->priv->searchmode==SEARCH_MODE_HEX)
    {
        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sdlg->priv->hex_mode), TRUE);
        set_hex_mode (sdlg);
    }
    else
    {
        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sdlg->priv->text_mode), TRUE);
        set_text_mode (sdlg);
    }

    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sdlg->priv->case_sensitive_checkbox), gnome_cmd_data.intviewer_defaults.case_sensitive);

    if (!gnome_cmd_data.intviewer_defaults.text_patterns.empty())
        gtk_entry_set_text(GTK_ENTRY(entry), gnome_cmd_data.intviewer_defaults.text_patterns.front());

    gtk_widget_grab_focus (sdlg->priv->entry);
}


static void search_dlg_destroy (GtkObject *object)
{
    g_return_if_fail (IS_GVIEWER_SEARCH_DLG (object));

    GViewerSearchDlg *w = GVIEWER_SEARCH_DLG (object);

    if (w->priv)
    {
        gnome_cmd_data.intviewer_defaults.search_mode = w->priv->searchmode;

        g_free (w->priv->search_text_string);
        w->priv->search_text_string = nullptr;

        g_free (w->priv);
        w->priv = nullptr;
    }

    if (GTK_OBJECT_CLASS (parent_class)->destroy)
        (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}


GType gviewer_search_dlg_get_type ()
{
  static GType ttt_type = 0;
  if (!ttt_type)
    {
      static const GTypeInfo ttt_info =
      {
        sizeof (GViewerSearchDlgClass),
        nullptr, // base_init
        nullptr, // base_finalize
        (GClassInitFunc) class_init,
        nullptr, // class_finalize
        nullptr, // class_data
        sizeof (GViewerSearchDlg),
        0, // n_preallocs
        (GInstanceInitFunc) search_dlg_init,
      };

      ttt_type = g_type_register_static (GTK_TYPE_DIALOG, "GViewerSearchDlg", &ttt_info, (GTypeFlags) 0);
    }

  return ttt_type;
}


GtkWidget* gviewer_search_dlg_new (GtkWindow *parent)
{
    auto dlg = static_cast<GViewerSearchDlg*> (g_object_new (gviewer_search_dlg_get_type(), nullptr));

    return GTK_WIDGET (dlg);
}
