/* OpenIDA - Outil d'analyse de fichiers binaires
 * gcodebuffer.h - prototypes pour l'affichage d'un fragment de code d'assemblage
 *
 * Copyright (C) 2010-2012 Cyrille Bagard
 *
 *  This file is part of OpenIDA.
 *
 *  OpenIDA 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.
 *
 *  OpenIDA 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 Foobar.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "gcodebuffer.h"


#include <malloc.h>
#include <string.h>


#include "delayed-int.h"
#include "../gtkext/iodamarshal.h"



/* -------------------------- PARCOURS DU CODE D'UN TAMPON -------------------------- */


#define G_TYPE_BUFFER_SCAN               g_buffer_scan_get_type()
#define G_BUFFER_SCAN(obj)               (G_TYPE_CHECK_INSTANCE_CAST((obj), g_buffer_scan_get_type(), GDelayedExport))
#define G_IS_BUFFER_SCAN(obj)            (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_buffer_scan_get_type()))
#define G_BUFFER_SCAN_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_BUFFER_SCAN, GDelayedExportClass))
#define G_IS_BUFFER_SCAN_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_BUFFER_SCAN))
#define G_BUFFER_SCAN_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_BUFFER_SCAN, GDelayedExportClass))


/* Ensembles binaires à désassembler (instance) */
typedef struct _GBufferScan
{
    GDelayedWork parent;                    /* A laisser en premier        */

    GCodeBuffer *buffer;                    /* Tampon à manipuler          */

    vmpa_t start;                           /* Début du parcours ou 0      */
    vmpa_t end;                             /* Fin du parcours ou VMPA_MAX */

    char *message;                          /* Message de progression      */

    process_line_fc process;                /* Fonction de traitement réel */
    void *user_data;                        /* Données à faire suivre      */

} GBufferScan;

/* Ensembles binaires à désassembler (classe) */
typedef struct _GBufferScanClass
{
    GDelayedWorkClass parent;               /* A laisser en premier        */

} GBufferScanClass;


/* Indique le type défini pour les tâches d'exportation différée. */
static GType g_buffer_scan_get_type(void);

/* Initialise la classe des tâches d'exportation différée. */
static void g_buffer_scan_class_init(GBufferScanClass *);

/* Initialise une tâche d'exportation différée. */
static void g_buffer_scan_init(GBufferScan *);

/* Crée une tâche d'exportation différée. */
static GBufferScan *g_buffer_scan_new(GCodeBuffer *, vmpa_t, vmpa_t, const char *, process_line_fc, void *);

/* Assure l'exportation en différé. */
static void g_buffer_scan_process(GBufferScan *, GtkExtStatusBar *);



/* -------------------------- TAMPON POUR CODE DESASSEMBLE -------------------------- */


/* Tampon pour code désassemblé (instance) */
struct _GCodeBuffer
{
    GObject parent;                         /* A laisser en premier        */

    GBufferLine **lines;                    /* Liste des lignes intégrées  */
    size_t count;                           /* Quantité en cache           */
    size_t used;                            /* Quantité utilisée           */

    size_t indent;                          /* Indentation des lignes      */

};

/* Tampon pour code désassemblé (classe) */
struct _GCodeBufferClass
{
    GObjectClass parent;                    /* A laisser en premier        */

};


/* Taille des allocations de masse */
#define LINE_ALLOC_BULK 20


/* Procède à l'initialisation d'une classe de tampon de code. */
static void g_code_buffer_class_init(GCodeBufferClass *);

/* Procède à l'initialisation d'un tampon pour code désassemblé. */
static void g_code_buffer_init(GCodeBuffer *);

/* Convertit une adresse en indice de ligne. */
static size_t g_code_buffer_get_index_from_address(GCodeBuffer *, vmpa_t);



/* ---------------------- VUE PARTICULIERE D'UN TAMPON DE CODE ---------------------- */


/* Vue d'un tampon pour code désassemblé (instance) */
struct _GBufferView
{
    GObject parent;                         /* A laisser en premier        */

    GCodeBuffer *buffer;                    /* Tampon de code visualisé    */
    vmpa_t start;                           /* Première ligne intégrée     */
    vmpa_t end;                             /* Dernière ligne intégrée     */

    gint line_height;                       /* Hauteur maximale des lignes */
    gint max_widths[BLC_COUNT];             /* Taille cachée des colonnes  */
    gint left_margin;                       /* Marge gauche + espace       */
    gint left_text;                         /* Début d'impression du code  */

    buffer_line_draw_fc drawing_extra;      /* Fonction d'accompagnement   */
    void *drawing_data;                     /* Donnée utilisateur          */
    GSList *highlighted;                    /* Segments mis en évidence    */

};

/* Vue d'un tampon pour code désassemblé (classe) */
struct _GBufferViewClass
{
    GObjectClass parent;                    /* A laisser en premier        */

    /* Signaux */

    void (* need_redraw) (GBufferView *);

};


#define WIDTHS_CACHED(view) ((view)->max_widths[0] != -1)


/* Procède à l'initialisation d'une classe de vue de tampon. */
static void g_buffer_view_class_init(GBufferViewClass *);

/* Procède à l'initialisation d'une vue d'un tampon pour code. */
static void g_buffer_view_init(GBufferView *);

/* Réinitialise le cache des largeurs de colonne calculées. */
static void g_buffer_view_reset_required_widths(GBufferView *);

/* Calcule les dimensions requises par une visualisation. */
static void g_buffer_view_compute_required_widths(GBufferView *);



/* ---------------------------------------------------------------------------------- */
/*                            PARCOURS DU CODE D'UN TAMPON                            */
/* ---------------------------------------------------------------------------------- */


/* Indique le type défini pour les tâches d'exportation différée. */
G_DEFINE_TYPE(GBufferScan, g_buffer_scan, G_TYPE_DELAYED_WORK);


/******************************************************************************
*                                                                             *
*  Paramètres  : klass = classe à initialiser.                                *
*                                                                             *
*  Description : Initialise la classe des tâches d'exportation différée.      *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_buffer_scan_class_init(GBufferScanClass *klass)
{

}


/******************************************************************************
*                                                                             *
*  Paramètres  : disass = instance à initialiser.                             *
*                                                                             *
*  Description : Initialise une tâche d'exportation différée.                 *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_buffer_scan_init(GBufferScan *disass)
{
    G_DELAYED_WORK(disass)->run = (run_task_fc)g_buffer_scan_process;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer  = tampon à manipuler.                                *
*                start   = première adresse visée ou 0.                       *
*                end     = dernière adresse visée ou VMPA_MAX.                *
*                message = message à afficher lors de la progression.         *
*                process = fonction assurant le traitement effectif.          *
*                data    = données utilisateur à faire suivre.                *
*                                                                             *
*  Description : Crée une tâche d'exportation différée.                       *
*                                                                             *
*  Retour      : Tâche créée.                                                 *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static GBufferScan *g_buffer_scan_new(GCodeBuffer *buffer, vmpa_t start, vmpa_t end, const char *message, process_line_fc process, void *data)
{
    GBufferScan *result;            /* Tâche à retourner           */

    result = g_object_new(G_TYPE_BUFFER_SCAN, NULL);

    result->buffer = buffer;
    g_object_ref(G_OBJECT(buffer));

    result->start = start;
    result->end = end;

    result->message = strdup(message);

    result->process = process;
    result->user_data = data;

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : scan      = parcours à mener.                                *
*                statusbar = barre de statut à tenir informée.                *
*                                                                             *
*  Description : Assure l'exportation en différé.                             *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_buffer_scan_process(GBufferScan *scan, GtkExtStatusBar *statusbar)
{
    size_t first;                           /* Première ligne visée        */
    size_t last;                            /* Dernière ligne visée + 1    */
    GBufferLine **lines;                    /* Liste des lignes à traiter  */
    guint id;                               /* Identifiant de statut       */
    size_t i;                               /* Boucle de parcours          */

    /* TODO : lock scan->buffer->lines */

    first = g_code_buffer_get_index_from_address(scan->buffer, scan->start);
    last = g_code_buffer_get_index_from_address(scan->buffer, scan->end);

    lines = scan->buffer->lines;

    id = gtk_extended_status_bar_push(statusbar, scan->message, true);

    for (i = first; i <= last; i++)
    {
        if (!scan->process(scan->buffer, lines[i], scan->user_data))
            break;

        gtk_extended_status_bar_update_activity(statusbar, id,
                                                (i - first) * 1.0 / (last - first));

    }

    /* TODO : unlock scan->buffer->lines */

    gtk_extended_status_bar_remove(statusbar, id);

    /* Avertit le commanditaire... */
    scan->process(scan->buffer, NULL, scan->user_data);

}



/* ---------------------------------------------------------------------------------- */
/*                            TAMPON POUR CODE DESASSEMBLE                            */
/* ---------------------------------------------------------------------------------- */


/* Détermine le type du composant de tampon pour code désassemblé. */
G_DEFINE_TYPE(GCodeBuffer, g_code_buffer, G_TYPE_OBJECT);


/******************************************************************************
*                                                                             *
*  Paramètres  : class = classe de composant GTK à initialiser.               *
*                                                                             *
*  Description : Procède à l'initialisation d'une classe de tampon de code.   *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_code_buffer_class_init(GCodeBufferClass *class)
{

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = composant GTK à initialiser.                        *
*                                                                             *
*  Description : Procède à l'initialisation d'un tampon pour code désassemblé.*
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_code_buffer_init(GCodeBuffer *buffer)
{

}


/******************************************************************************
*                                                                             *
*  Paramètres  : -                                                            *
*                                                                             *
*  Description : Crée un nouveau composant de tampon pour code désassemblé.   *
*                                                                             *
*  Retour      : Composant GTK créé.                                          *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GCodeBuffer *g_code_buffer_new(void)
{
    GCodeBuffer *result;                    /* Composant à retourner       */

    result = g_object_new(G_TYPE_CODE_BUFFER, NULL);

    return result;

}

/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = composant GTK à mettre à jour.                      *
*                addr   = adresse où va se situer la ligne.                   *
*                                                                             *
*  Description : Convertit une adresse en indice de ligne.                    *
*                                                                             *
*  Retour      : Indice de l'adresse trouvée, ou 0 en cas d'échec.            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static size_t g_code_buffer_get_index_from_address(GCodeBuffer *buffer, vmpa_t addr)
{
    size_t result;                          /* Indice à retourner          */

    if (addr == VMPA_MAX)
        return (buffer->used > 0 ? buffer->used - 1 : 0);

    for (result = 0; result < buffer->used; result++)
        if (g_buffer_line_get_address(buffer->lines[result]) == addr)
            break;

    if (result == buffer->used)
        result = 0;

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = composant GTK à mettre à jour.                      *
*                addr   = adresse où va se situer la ligne.                   *
*                                                                             *
*  Description : Ajoute une nouvelle ligne à un tampon pour code désassemblé. *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GBufferLine *g_code_buffer_append_new_line(GCodeBuffer *buffer, vmpa_t addr)
{
    GBufferLine *result;                    /* Instance à retourner        */
    size_t i;                               /* Boucle de parcours          */

    if (buffer->used == buffer->count)
    {
        buffer->count += LINE_ALLOC_BULK;
        buffer->lines = (GBufferLine **)realloc(buffer->lines,
                                                buffer->count * sizeof(GBufferLine *));
    }

    result = g_buffer_line_new(addr);
    buffer->lines[buffer->used++] = result;

    for (i = 0; i < buffer->indent; i++)
        g_buffer_line_insert_text(result, BLC_ASSEMBLY_HEAD, "    ", 4, RTT_RAW);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = composant GTK à mettre à jour.                      *
*                addr   = adresse où retrouver la ligne recherchée.           *
*                                                                             *
*  Description : Retrouve une ligne au sein d'un tampon avec une adresse.     *
*                                                                             *
*  Retour      : Line retrouvée ou NULL en cas d'échec.                       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GBufferLine *g_code_buffer_find_line_by_addr(const GCodeBuffer *buffer, vmpa_t addr)
{
    GBufferLine *result;                    /* Instance à retourner        */
    size_t i;                               /* Boucle de parcours          */

    result = NULL;

    /* TODO : coder un parcours plus optimal ! */

    for (i = 0; i < buffer->used && result == NULL; i++)
        if (g_buffer_line_get_address(buffer->lines[i]) == addr)
            result = buffer->lines[i];

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = composant GTK à mettre à jour.                      *
*                                                                             *
*  Description : Augmente l'indentation des prochaines lignes.                *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_code_buffer_inc_indentation(GCodeBuffer *buffer)
{
    buffer->indent++;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = composant GTK à mettre à jour.                      *
*                                                                             *
*  Description : Diminue l'indentation des prochaines lignes.                 *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_code_buffer_dec_indentation(GCodeBuffer *buffer)
{
    /* BUG_ON(buffer->indent == 0) */

    buffer->indent--;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = tampon de données à utiliser.                       *
*                start   = première adresse visée ou 0.                       *
*                end     = dernière adresse visée ou VMPA_MAX.                *
*                message = message à afficher lors de la progression.         *
*                process = fonction assurant le traitement effectif.          *
*                data    = données utilisateur à faire suivre.                *
*                                                                             *
*  Description : Lance un parcours des différentes lignes du tampon de code.  *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_buffer_code_scan(GCodeBuffer *buffer, vmpa_t start, vmpa_t end, const char *message, process_line_fc process, void *data)
{
    GBufferScan *scan;                      /* Procédure de parcours       */
    GWorkQueue *queue;                      /* Gestionnaire de différés    */

    scan = g_buffer_scan_new(buffer, start, end, message, process, data);

    queue = get_work_queue();
    g_work_queue_schedule_work(queue, G_DELAYED_WORK(scan));

}



/* ---------------------------------------------------------------------------------- */
/*                        VUE PARTICULIERE D'UN TAMPON DE CODE                        */
/* ---------------------------------------------------------------------------------- */


/* Détermine le type de la vue d'un tampon pour code désassemblé. */
G_DEFINE_TYPE(GBufferView, g_buffer_view, G_TYPE_OBJECT);


/******************************************************************************
*                                                                             *
*  Paramètres  : class = classe de composant GTK à initialiser.               *
*                                                                             *
*  Description : Procède à l'initialisation d'une classe de vue de tampon.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_buffer_view_class_init(GBufferViewClass *class)
{
    g_signal_new("need-redraw",
                 G_TYPE_BUFFER_VIEW,
                 G_SIGNAL_RUN_LAST,
                 G_STRUCT_OFFSET(GBufferViewClass, need_redraw),
                 NULL, NULL,
                 g_cclosure_user_marshal_VOID__OBJECT,
                 G_TYPE_NONE, 0);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = composant GTK à initialiser.                        *
*                                                                             *
*  Description : Procède à l'initialisation d'une vue d'un tampon pour code.  *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_buffer_view_init(GBufferView *buffer)
{

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = tampon à représenter à l'écran.                     *
*                                                                             *
*  Description : Crée une nouvelle vue d'un tampon pour code désassemblé.     *
*                                                                             *
*  Retour      : Composant GTK créé.                                          *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GBufferView *g_buffer_view_new(GCodeBuffer *buffer)
{
    GBufferView *result;                    /* Composant à retourner       */

    result = g_object_new(G_TYPE_BUFFER_VIEW, NULL);

    g_object_ref(G_OBJECT(buffer));

    result->buffer = buffer;
    result->start = 0;
    result->end = VMPA_MAX;

    g_buffer_view_reset_required_widths(result);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view  = visualisateur à mettre à jour.                       *
*                first = première ligne à imprimer.                           *
*                last  = première ligne hors cadre.                           *
*                                                                             *
*  Description : Restreint le champ d'application de l'affichage.             *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_buffer_view_restrict(GBufferView *view, vmpa_t start, vmpa_t end)
{
    view->start = start;
    view->end = end;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view  = visualisateur à mettre à jour.                       *
*                first = première ligne à imprimer ou NULL. [OUT]             *
*                last  = première ligne hors cadre ou NULL. [OUT]             *
*                                                                             *
*  Description : Indique le champ d'application de l'affichage.               *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_buffer_view_get_restrictions(GBufferView *view, vmpa_t *start, vmpa_t *end)
{
    if (start != NULL) *start = view->start;
    if (end != NULL) *end = view->end;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view = visualisateur à consulter.                            *
*                                                                             *
*  Description : Fournit le tampon de code lié à un visualisateur donné.      *
*                                                                             *
*  Retour      : Tampon de code associé au gestionnaire d'affichage.          *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GCodeBuffer *g_buffer_view_get_buffer(const GBufferView *view)
{
    return view->buffer;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view = visualisation à consulter.                            *
*                                                                             *
*  Description : Réinitialise le cache des largeurs de colonne calculées.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_buffer_view_reset_required_widths(GBufferView *view)
{
    unsigned int i;                         /* Boucle de parcours          */

    for (i = 0; i < BLC_COUNT; i++)
        view->max_widths[i] = -1;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view = visualisation à consulter.                            *
*                                                                             *
*  Description : Calcule les dimensions requises par une visualisation.       *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_buffer_view_compute_required_widths(GBufferView *view)
{
    GBufferLine **lines;                    /* Liste des lignes à traiter  */
    size_t first;                           /* Première ligne intégrée     */
    size_t last;                            /* Dernière ligne intégrée     */
    size_t i;                               /* Boucle de parcours #1       */
    unsigned int j;                         /* Boucle de parcours #2       */
    gint width;                             /* Largeur d'une colonne       */

    lines = view->buffer->lines;

    first = g_code_buffer_get_index_from_address(view->buffer, view->start);
    last = g_code_buffer_get_index_from_address(view->buffer, view->end);

    view->line_height = 17;

    view->left_margin = 2 * view->line_height;
    view->left_text = 2.5 * view->line_height;

    for (i = first; i <= last; i++)
        for (j = 0; j < BLC_COUNT; j++)
        {
            width = g_buffer_line_get_width(lines[i], j);
            view->max_widths[j] = MAX(view->max_widths[j], width);
        }

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view = visualisation à consulter.                            *
*                                                                             *
*  Description : Fournit la hauteur d'impression d'une ligne visualisée.      *
*                                                                             *
*  Retour      : Hauteur de ligne en pixel.                                   *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

gint g_buffer_view_get_line_height(GBufferView *view)
{
    if (!WIDTHS_CACHED(view))
        g_buffer_view_compute_required_widths(view);

    return view->line_height;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view   = visualisation à consulter.                          *
*                width  = largeur requise pour une pleine visualisation. [OUT]*
*                height = hauteur requise pour une pleine visualisation. [OUT]*
*                addr   = indique si les positions doivent être affichées.    *
*                code   = indique si le code binaire doit être affiché.       *
*                                                                             *
*  Description : Fournit les dimensions requises par une visualisation.       *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_buffer_view_get_size(GBufferView *view, gint *width, gint *height, bool addr, bool code)
{
    unsigned int i;                         /* Boucle de parcours          */
    size_t first;                           /* Première ligne intégrée     */
    size_t last;                            /* Dernière ligne intégrée     */

    *width = 0;
    *height = view->line_height;

    if (!WIDTHS_CACHED(view))
        g_buffer_view_compute_required_widths(view);

    for (i = 0; i < BLC_COUNT; i++)
    {
        if (i == BLC_ADDRESS && !addr) continue;
        if (i == BLC_BINARY && !code) continue;

        *width += view->max_widths[i];

    }

    first = g_code_buffer_get_index_from_address(view->buffer, view->start);
    last = g_code_buffer_get_index_from_address(view->buffer, view->end);

    *height *= (last - first + 1);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view = vue de tampon à mettre à jour.                        *
*                                                                             *
*  Description : Supprime toute mise en évidence de segments.                 *
*                                                                             *
*  Retour      : true si un besoin d'actualisation d'affichage se fait sentir.*
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool g_buffer_view_unhighlight_segments(GBufferView *view)
{
    GSList *iter;                           /* Boucle de parcours          */
    GBufferSegment *segment;                /* Segment visé par le pointeur*/

    if (g_slist_length(view->highlighted) == 0)
        return false;

    for (iter = view->highlighted; iter != NULL; iter = view->highlighted)
    {
        segment = G_BUFFER_SEGMENT(iter->data);
        g_buffer_segment_set_style(segment, SRS_CLASSIC);

        g_object_unref(G_OBJECT(segment));

        view->highlighted = g_slist_remove_link(view->highlighted, iter);

    }

    return true;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view  = vue de tampon à mettre à jour.                       *
*                x     = abscisse de la zone principale à traiter.            *
*                y     = ordonnée de la zone principale à traiter.            *
*                                                                             *
*  Description : Surligne tous les segments similaires à celui sous la souris.*
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_buffer_view_highlight_segments(GBufferView *view, gint x, gint y)
{
    GBufferLine *line;                      /* Ligne sous le pointeur      */
    GBufferSegment *segment;                /* Segment visé par le pointeur*/
    bool need_redraw;                       /* Besoin d'actualisation ?    */
    size_t first;                           /* Première ligne intégrée     */
    size_t last;                            /* Dernière ligne intégrée     */
    size_t i;                               /* Boucle de parcours          */

    line = g_buffer_view_find_line_at(view, y);
    if (line == NULL) return;


    do
    {
        vmpa_t addr;
        addr = g_buffer_line_get_address(line);

        printf(" ... x2 clic at 0x%08lx\n", addr);

    }
    while (0);

    x -= view->left_text;
    if (x < 0) return;

    segment = g_buffer_line_get_segment_at(line, view->max_widths, x);
    printf(" ... seg @%d ? %p\n", x, segment);
    if (segment == NULL) return;


    printf("text :: '%s'\n", g_buffer_segment_get_text(segment));


    if (view->highlighted != NULL)
        need_redraw = g_buffer_view_unhighlight_segments(view);
    else
        need_redraw = false;

    first = g_code_buffer_get_index_from_address(view->buffer, view->start);
    last = g_code_buffer_get_index_from_address(view->buffer, view->end);

    for (i = first; i < last; i++)
        view->highlighted = g_buffer_line_highlight_all_same_segments(view->buffer->lines[i],
                                                                      view->highlighted, segment);

    if (g_slist_length(view->highlighted) > 0)
        need_redraw = true;

    if (need_redraw)
        g_signal_emit_by_name(view, "need-redraw");

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view   = visualisation à mettre à jour.                      *
*                method = procédure à appeler à chaque dessin de ligne.       *
*                data   = donnée utilisateur à passer lors des appels.        *
*                                                                             *
*  Description : Définit à une procédure à appeler lors des dessins de ligne. *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_buffer_view_define_extra_drawing(GBufferView *view, buffer_line_draw_fc method, void *data)
{
    view->drawing_extra = method;
    view->drawing_data = data;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view   = visualisation à représenter.                        *
*                event  = informations liées à l'événement.                   *
*                gc     = contexte graphique à utiliser pour les pinceaux.    *
*                fake_x = abscisse réelle du point 0 à l'écran.               *
*                fake_y = ordonnée réelle du point 0 à l'écran.               *
*                addr   = indique si les positions doivent être affichées.    *
*                code   = indique si le code binaire doit être affiché.       *
*                                                                             *
*  Description : Imprime la visualisation du tampon de code désassemblé.      *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_buffer_view_draw(const GBufferView *view, const GdkEventExpose *event, GdkGC *gc, gint fake_x, gint fake_y, bool addr, bool code)
{
    GdkDrawable *drawable;                  /* Surface de dessin           */
    gint real_x;                            /* Abscisse réelle pour tampon */
    gint real_y;                            /* Ordonnée réelle pour tampon */

    size_t first;                           /* Première ligne visée        */
    size_t end;                             /* Dernière ligne avant limite */
    size_t last;                            /* Dernière ligne visée + 1    */
    gint y;                                 /* Point de départ + décallage */


    GBufferLine **lines;                    /* Liste des lignes à traiter  */
    size_t i;                               /* Boucle de parcours          */


    drawable = GDK_DRAWABLE(event->window);


    real_x = fake_x + view->left_text;
    real_y = fake_y + event->area.y;



    first = g_code_buffer_get_index_from_address(view->buffer, view->start);
    first += (real_y / view->line_height);

    last = first + (event->area.height / view->line_height);
    if (event->area.height % view->line_height > 0) last++;

    end = g_code_buffer_get_index_from_address(view->buffer, view->end);
    last = MIN(last, end);

    y = event->area.y - (real_y % view->line_height);

    lines = view->buffer->lines;

    if (view->buffer->used > 0)
        for (i = first; i <= last; i++)
        {
            /* TODO : skip if... */

            if (view->drawing_extra != NULL)
                view->drawing_extra(lines[i], drawable, gc, fake_x, y, view->drawing_data);

            g_buffer_line_draw(lines[i], drawable, gc, view->max_widths, real_x, y, addr, code);

            y += view->line_height;

        }

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view = visualisation à consulter.                            *
*                y    = ordonnée comprise dans la ligne recherchée.           *
*                                                                             *
*  Description : Fournit la ligne présente à une ordonnée donnée.             *
*                                                                             *
*  Retour      : Ligne retrouvée ou NULL si aucune.                           *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GBufferLine *g_buffer_view_find_line_at(GBufferView *view, gint y)
{
    gint lheight;                           /* Hauteur d'une ligne         */
    size_t index;                           /* Indice attendu              */

    lheight = g_buffer_view_get_line_height(view);
    index = y / lheight;

    return (index < view->buffer->used ? view->buffer->lines[index] : NULL);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view = composant GTK à consulter.                            *
*                addr = adresse à présenter à l'écran.                        *
*                x    = position horizontale au sein du composant. [OUT]      *
*                y    = position verticale au sein du composant. [OUT]        *
*                                                                             *
*  Description : Indique la position d'affichage d'une adresse donnée.        *
*                                                                             *
*  Retour      : true si l'adresse fait partie du composant, false sinon.     *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool g_buffer_view_get_address_coordinates(GBufferView *view, vmpa_t addr, gint *x, gint *y)
{
    gint lheight;                           /* Hauteur d'une ligne         */
    vmpa_t current;                         /* Adresse parcourue           */
    size_t first;                           /* Première ligne intégrée     */
    size_t last;                            /* Dernière ligne intégrée     */
    size_t i;                               /* Boucle de parcours          */

    *x = 0;
    *y = 0;

    lheight = g_buffer_view_get_line_height(view);
    current = VMPA_MAX;

    first = g_code_buffer_get_index_from_address(view->buffer, view->start);
    last = g_code_buffer_get_index_from_address(view->buffer, view->end);

    for (i = first; i < last; i++)
    {
        current = g_buffer_line_get_address(view->buffer->lines[i]);

        if (current == addr)
            break;

        if (current > addr)
            return false;

        *y += lheight;

    }

    return (current == addr);

}