/* Chrysalide - Outil d'analyse de fichiers binaires
 * bookmark.c - boîte de dialogue pour les sauts à une adresse donnée
 *
 * Copyright (C) 2015-2017 Cyrille Bagard
 *
 *  This file is part of Chrysalide.
 *
 *  Chrysalide 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 of the License, or
 *  (at your option) any later version.
 *
 *  Chrysalide 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 Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "bookmark.h"


#include <ctype.h>
#include <string.h>


#include <i18n.h>


#include "../../analysis/db/items/bookmark.h"
#include "../../gtkext/easygtk.h"



/* Filtre les adresses en hexadécimal pendant l'édition. */
static void filter_addresses(GtkEntry *, const gchar *, gint, gint *, gpointer);

/* Clôture l'édition d'une adresse. */
static void validate_addresses(GtkEntry *, GtkDialog *);



/******************************************************************************
*                                                                             *
*  Paramètres  : entry    = composant GTK concerné par la procédure.          *
*                text     = nouveau texte inséré.                             *
*                length   = taille de ce texte.                               *
*                position = point d'insertion.                                *
*                data     = adresse non utilisée ici.                         *
*                                                                             *
*  Description : Filtre les adresses en hexadécimal pendant l'édition.        *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void filter_addresses(GtkEntry *entry, const gchar *text, gint length, gint *position, gpointer data)
{
    gboolean has_hex;                       /* Préfixe '0x' déjà présent ? */
    gchar *filtered;                        /* Contenu nouveau approuvé    */
    gint count;                             /* Nouvelle taille validée     */
    gint i;                                 /* Boucle de parcours          */

    /**
     * On cherche à empêcher l'édition avant un '0x' présent,
     * ce qui viendrait fausser le fitrage.
     */
    has_hex = g_str_has_prefix(gtk_entry_get_text(entry), "0x");

    filtered = g_new(gchar, length);

    count = 0;

    for (i = 0; i < length; i++)
        switch (text[i])
        {
            case '0' ... '9':
            case 'a' ... 'f':
                if (!has_hex || ((i + *position) >= 2))
                    filtered[count++] = text[i];
                break;
            case 'A' ... 'F':
                if (!has_hex || ((i + *position) >= 2))
                    filtered[count++] = tolower(text[i]);
                break;
            case 'x':
            case 'X':
                if ((i + *position) == 1)
                    filtered[count++] = 'x';
                break;
        }

    if (count > 0)
    {
        g_signal_handlers_block_by_func(G_OBJECT(entry), G_CALLBACK(filter_addresses), data);
        gtk_editable_insert_text(GTK_EDITABLE(entry), filtered, count, position);
        g_signal_handlers_unblock_by_func(G_OBJECT(entry), G_CALLBACK(filter_addresses), data);
    }

    g_signal_stop_emission_by_name(G_OBJECT(entry), "insert_text");

    g_free(filtered);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : entry  = composant GTK concerné par la procédure.            *
*                dialog = boîte de dialogue à valider.                        *
*                                                                             *
*  Description : Clôture l'édition d'une adresse.                             *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void validate_addresses(GtkEntry *entry, GtkDialog *dialog)
{
    gtk_dialog_response(dialog, GTK_RESPONSE_OK);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : parent = fenêtre parente à surpasser.                        *
*                addr   = localisation du point à consigner.                  *
*                                                                             *
*  Description : Construit la fenêtre de création de signet.                  *
*                                                                             *
*  Retour      : Adresse de la fenêtre mise en place.                         *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GtkWidget *create_bookmark_dialog(GtkWindow *parent, const vmpa2t *addr)
{
    GtkWidget *result;                      /* Fenêtre à renvoyer          */
    GtkWidget *dlgvbox;                     /* Zone principale de la boîte */
    GtkWidget *vbox;                        /* Support à construire #1     */
    GtkWidget *frame;                       /* Support avec encadrement    */
    GtkWidget *sub_vbox;                    /* Support à construire #2     */
    GtkWidget *hbox;                        /* Support à construire #3     */
    GtkWidget *label;                       /* Message d'introduction      */
    GtkWidget *entry;                       /* Zone de saisie principale   */
    GtkWidget *radio;                       /* Définition de localisation  */
    VMPA_BUFFER(target);                    /* Désignation humaine de cible*/

    result = gtk_dialog_new();
    gtk_window_set_title(GTK_WINDOW(result), _("Add a bookmark"));
    gtk_window_set_position(GTK_WINDOW(result), GTK_WIN_POS_CENTER);
    gtk_window_set_modal(GTK_WINDOW(result), TRUE);
    gtk_window_set_type_hint(GTK_WINDOW(result), GDK_WINDOW_TYPE_HINT_DIALOG);

    dlgvbox = gtk_dialog_get_content_area(GTK_DIALOG(result));
    gtk_widget_show(dlgvbox);

    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
    gtk_widget_show(vbox);
    gtk_box_pack_start(GTK_BOX(dlgvbox), vbox, TRUE, TRUE, 0);
    gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);

    /* Localisation dans l'espace */

    sub_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
    gtk_widget_show(sub_vbox);

    frame = qck_create_frame(_("<b>Localisation</b>"), sub_vbox, 8, 0, 12, 0);
    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);

    /* 1) Adresse */

    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
    gtk_widget_show(hbox);
    gtk_box_pack_start(GTK_BOX(sub_vbox), hbox, FALSE, TRUE, 0);

    label = qck_create_label(NULL, NULL, _("Target:"));
    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);

    entry = qck_create_entry(G_OBJECT(result), "addr", NULL);
    //g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(forbid_text_empty_entry), assistant);
    gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, TRUE, 0);

    /* 2) Type d'adresse */

    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
    gtk_widget_show(hbox);
    gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
    gtk_container_set_border_width(GTK_CONTAINER(hbox), 8);

    radio = qck_create_radio_button(G_OBJECT(result), "phys", _("Value is physical offset"),
                                          NULL, NULL, NULL);
    gtk_box_pack_start(GTK_BOX(hbox), radio, TRUE, TRUE, 0);

    radio = qck_create_radio_button(G_OBJECT(result), "virt", _("Value is virtual address"),
                                          GTK_RADIO_BUTTON(radio), NULL, NULL);
    gtk_box_pack_start(GTK_BOX(hbox), radio, TRUE, TRUE, 0);

    /* Commentaire éventuel */

    entry = qck_create_entry(G_OBJECT(result), "comment", NULL);

    frame = qck_create_frame(_("<b>Optional comment</b>"), entry, 8, 0, 12, 0);
    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);

    /* Zone de validation */

    gtk_dialog_add_button(GTK_DIALOG(result), _("_Cancel"), GTK_RESPONSE_CANCEL);
    gtk_dialog_add_button(GTK_DIALOG(result), _("_Ok"), GTK_RESPONSE_OK);

    /* Remplissage avec les valeurs fournies */

    entry = GTK_WIDGET(g_object_get_data(G_OBJECT(result), "addr"));

    if (addr != NULL && has_virt_addr(addr))
    {
        vmpa2_virt_to_string(addr, MDS_UNDEFINED, target, NULL);
        radio = GTK_WIDGET(g_object_get_data(G_OBJECT(result), "virt"));
    }

    else if (addr != NULL && has_phys_addr(addr))
    {
        vmpa2_virt_to_string(addr, MDS_UNDEFINED, target, NULL);
        radio = GTK_WIDGET(g_object_get_data(G_OBJECT(result), "virt"));
    }

    else
        radio = NULL;

    if (radio == NULL)
    {
        gtk_entry_set_text(GTK_ENTRY(entry), "0x");
        gtk_editable_set_position(GTK_EDITABLE(entry), -1);
        gtk_widget_grab_focus(entry);
    }

    else
    {
        gtk_entry_set_text(GTK_ENTRY(entry), target);
        gtk_editable_set_position(GTK_EDITABLE(entry), -1);

        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE);

    }

    if (radio != NULL)
    {
        entry = GTK_WIDGET(g_object_get_data(G_OBJECT(result), "comment"));
        gtk_widget_grab_focus(entry);
    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : dialog = boîte de dialogue ayant reçu une validation.        *
*                                                                             *
*  Description : Fournit le signet conçu via la saisie de l'utilisateur.      *
*                                                                             *
*  Retour      : Adresse reccueillie par la boîte de dialogue.                *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GDbItem *get_item_from_bookmark_dialog(GtkWidget *dialog)
{
    GDbItem *result;                        /* Signet nouveau à retourner  */
    GObject *ref;                           /* Espace de référencements    */
    vmpa2t *orig;                           /* Copie de valeur originale   */
    vmpa2t addr;                            /* Localisation finale utilisée*/
    GtkWidget *entry;                       /* Zone de saisie principale   */
    const gchar *text;                      /* Adresse en version texte    */
    GtkToggleButton *radio;                 /* Définition de localisation  */

    ref = G_OBJECT(dialog);

    /* Si la valeur d'origine a été conservée intacte... */

    orig = (vmpa2t *)g_object_get_data(ref, "orig");

    if (orig == NULL)
    {
        entry = GTK_WIDGET(g_object_get_data(ref, "addr"));
        text = gtk_entry_get_text(GTK_ENTRY(entry));

        radio = GTK_TOGGLE_BUTTON(g_object_get_data(ref, "phys"));

        if (gtk_toggle_button_get_active(radio))
            orig = string_to_vmpa_phy(text);
        else
            orig = string_to_vmpa_virt(text);



    }

    copy_vmpa(&addr, orig);
    delete_vmpa(orig);

    /* Récupération du commentaire éventuel */

    entry = GTK_WIDGET(g_object_get_data(ref, "comment"));
    text = gtk_entry_get_text(GTK_ENTRY(entry));

    /* Mise en place du signet défini */

    if (strlen(text) > 0)
        result = G_DB_ITEM(g_db_bookmark_new(&addr, text));
    else
        result = G_DB_ITEM(g_db_bookmark_new(&addr, NULL));

    return result;

}