/* Chrysalide - Outil d'analyse de fichiers binaires
 * gcodebuffer.h - prototypes pour l'affichage d'un fragment de code d'assemblage
 *
 * Copyright (C) 2010-2014 Cyrille Bagard
 *
 *  This file is part of Chrysalide.
 *
 *  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 <limits.h>
#include <malloc.h>
#include <string.h>


#include "chrysamarshal.h"
#include "delayed-int.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        */

    BufferLineColumn main_column;           /* Colonne principale          */

    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(const GCodeBuffer *, const vmpa2t *, bool);

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



/* ---------------------- 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  */
    gint last_width;                        /* Plus grande col. de fusion  */
    BufferLineColumn last_merge;            /* Colonne de fusion extrême   */

    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 HEIGHT_CACHED(view) ((view)->line_height != -1)
#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 de la hauteur des lignes. */
static void g_buffer_view_reset_required_height(GBufferView *);

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

/* Calcule les largeurs requises par une visualisation. */
static void g_buffer_view_compute_required_widths(GBufferView *, const bool *);



/* ---------------------------------------------------------------------------------- */
/*                            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  */
    bstatus_id_t 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, true);
    last = g_code_buffer_get_index_from_address(scan->buffer, scan->end, false);

    lines = scan->buffer->lines;

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

    if (scan->buffer->used > 0)
        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  : main = colonne à référencer comme étant la principale.       *
*                                                                             *
*  Description : Crée un nouveau composant de tampon pour code désassemblé.   *
*                                                                             *
*  Retour      : Composant GTK créé.                                          *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

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

    result = g_object_new(G_TYPE_CODE_BUFFER, NULL);

    result->main_column = main;

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = composant GTK à mettre à jour.                      *
*                addr   = adresse où va se situer la ligne.                   *
*                first  = indique si on l'arrête à la première ou la dernière.*
*                                                                             *
*  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(const GCodeBuffer *buffer, const vmpa2t *addr, bool first)
{
    size_t result;                          /* Indice à retourner          */

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

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

    return (first ? 0 : buffer->used - 1);


#if 0

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

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

#endif

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = composant GTK à mettre à jour.                      *
*                addr   = adresse où va se situer la ligne.                   *
*                first  = indique si on l'arrête à la première ou la dernière.*
*                                                                             *
*  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, const vmpa2t *addr, bool first)
{
    size_t result;                          /* Indice à retourner          */

    result = _g_code_buffer_get_index_from_address(buffer, addr, first);

    /**
     * Par commodités, on évite certaines instructions en cas d'échec dans les
     * fonctions d'appels : la condition des boucles utilisant l'indice retourné (0)
     * fait son office directement !
     */
    if (result == buffer->used)
        result = 0;

    return result;

}


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

GBufferLine *g_code_buffer_append_new_line(GCodeBuffer *buffer, const mrange_t *range)
{
    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(range, buffer->main_column);
    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ù va se situer la ligne.                   *
*                before = emplacement de l'insertion.                         *
*                                                                             *
*  Description : Ajoute une nouvelle ligne à un tampon pour code désassemblé. *
*                                                                             *
*  Retour      : Nouvelle ligne vierge à écrire.                              *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GBufferLine *g_code_buffer_insert_at(GCodeBuffer *buffer, vmpa_t addr, bool before)
{
    GBufferLine *result;                    /* Instance à retourner        */
    size_t index;                           /* Indice de la ligne visée    */

    index = _g_code_buffer_get_index_from_address(buffer, addr, true /* FIXME : after */);
    if (index == buffer->used) return NULL;

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

    if (before)
    {
        memmove(&buffer->lines[index + 1], &buffer->lines[index],
                sizeof(GBufferLine *) * (buffer->used - index));

        buffer->used++;

        result = g_buffer_line_new(addr, buffer->main_column);
        buffer->lines[index] = result;

    }


    else
    /* FIXME */
        ;


    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = composant GTK à mettre à jour.                      *
*                line   = point d'insertion.                                  *
*                                                                             *
*  Description : Ajoute une nouvelle ligne à un tampon pour code désassemblé. *
*                                                                             *
*  Retour      : Nouvelle ligne vierge à écrire.                              *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GBufferLine *g_code_buffer_insert_after(GCodeBuffer *buffer, GBufferLine *line)
{

    /* FIXME */

    return NULL;


}


/******************************************************************************
*                                                                             *
*  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 index;                           /* Indice de la ligne visée    */

    index = _g_code_buffer_get_index_from_address(buffer, addr, true);

    if (index == buffer->used)
        result = NULL;
    else
        result = buffer->lines[index];

    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)
{
    g_buffer_view_reset_required_height(buffer);
    g_buffer_view_reset_required_widths(buffer);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = tampon à représenter à l'écran.                     *
*                widget = composant GTK de destination pour le rendu.         *
*                                                                             *
*  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;

    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 de la hauteur des lignes.              *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_buffer_view_reset_required_height(GBufferView *view)
{
    view->line_height = -1;

}


/******************************************************************************
*                                                                             *
*  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 à mettre à jour.                        *
*                                                                             *
*  Description : Calcule la hauteur requise par une visualisation.            *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_buffer_view_compute_required_height(GBufferView *view)
{
    view->line_height = 17;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view = visualisation à mettre à jour.                        *
*                display = règles d'affichage des colonnes modulables.        *
*                                                                             *
*  Description : Calcule les largeurs requises par une visualisation.         *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_buffer_view_compute_required_widths(GBufferView *view, const bool *display)
{
    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       */
    BufferLineColumn merge;                 /* Début de fusion de colonnes */

    if (!HEIGHT_CACHED(view))
        g_buffer_view_compute_required_height(view);

    lines = view->buffer->lines;

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

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

    view->last_width = 0;
    view->last_merge = BLC_INVALID;

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

            width = g_buffer_line_get_merge_width(lines[i], &merge, display);
            view->last_width = MAX(view->last_width, width);
            if (merge != BLC_COUNT)
            {
                if (view->last_merge == BLC_INVALID)
                    view->last_merge = merge;
                else
                    view->last_merge = MAX(view->last_merge, merge);
            }

        }

    if (view->last_merge == BLC_INVALID)
        view->last_merge = BLC_COUNT;

}


/******************************************************************************
*                                                                             *
*  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 (!HEIGHT_CACHED(view))
        g_buffer_view_compute_required_height(view);

    return view->line_height;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view    = visualisation à consulter.                         *
*                display = règles d'affichage des colonnes modulables.        *
*                                                                             *
*  Description : Fournit la largeur requise par une visualisation.            *
*                                                                             *
*  Retour      : Dimension calculée.                                          *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

gint g_buffer_view_get_width(GBufferView *view, const bool *display)
{
    gint result;                            /* Taille à retourner          */
    gint col_width;                         /* Calcul selon les colonnes   */
    gint full_width;                        /* Calcul selon les fusions    */
    BufferLineColumn i;                     /* Boucle de parcours          */

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

    result = view->left_text;

    col_width = 0;
    full_width = 0;

    /* Première méthode */

    for (i = 0; i < BLC_COUNT; i++)
    {
        if (i < BLC_DISPLAY && !display[i]) continue;

        col_width += view->max_widths[i];

        if ((i + 1) < BLC_COUNT)
            col_width += COL_MARGIN;

    }

    /* Seconde méthode */

    for (i = 0; i < view->last_merge; i++)
    {
        if (i < BLC_DISPLAY && !display[i]) continue;

        full_width += view->max_widths[i];

        if ((i + 1) < view->last_merge)
            full_width += COL_MARGIN;

    }

    full_width += view->last_width + COL_MARGIN;

    /* Mise en concurrence et poursuite... */

    result += + MAX(col_width, full_width);

    return result;

}



/******************************************************************************
*                                                                             *
*  Paramètres  : view = visualisation à consulter.                            *
*                                                                             *
*  Description : Fournit la hauteur requise par une visualisation.            *
*                                                                             *
*  Retour      : Dimension calculée.                                          *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

gint g_buffer_view_get_height(const GBufferView *view)
{
    gint result;                            /* Taille à retourner          */
    size_t first;                           /* Première ligne intégrée     */
    size_t last;                            /* Dernière ligne intégrée     */

    result = view->line_height;

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

    result *= (last - first + 1);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view    = vue de tampon à mettre à jour.                     *
*                line    = ligne correspondant à la position.                 *
*                index   = indice de cette même ligne dans le tampon.         *
*                x       = abscisse de la zone principale à traiter.          *
*                display = règles d'affichage des colonnes modulables.        *
*                caret   = position du curseur à construire. [OUT]            *
*                                                                             *
*  Description : Calcule la position idéale de curseur pour un point donné.   *
*                                                                             *
*  Retour      : Adresse si une a pu être déterminée, NULL sinon.             *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

const vmpa2t *g_buffer_view_compute_caret(GBufferView *view, GBufferLine *line, size_t index, gint x, const bool *display, GdkRectangle *caret)
{
    gint tmp_x;                             /* Copie de travail modifiable */
    GBufferSegment *segment;                /* Segment visé par le pointeur*/
    size_t first;                           /* Première ligne intégrée     */

    tmp_x = x;

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

    segment = g_buffer_line_get_segment_at(line, view->max_widths, display, &tmp_x, GDK_SCROLL_LEFT, true);
    if (segment == NULL) printf(" -- no segment\n");
    if (segment == NULL) return NULL;

    printf("\n[BASE]  tronc = %d   reste = %d   dernier = %d   largeur = %d\n",
           x - tmp_x, tmp_x, g_buffer_segment_get_caret_position(segment, tmp_x),
           g_buffer_segment_get_width(segment));

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

    caret->x = (x - tmp_x) + g_buffer_segment_get_caret_position(segment, tmp_x);

    first = g_code_buffer_get_index_from_address(view->buffer, view->start, true);
    caret->y = (index - first) * view->line_height;

    caret->width = 2;
    caret->height = view->line_height;

    return get_mrange_addr(g_buffer_line_get_range(line));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : line    = ligne à venir consulter.                           *
*                caret   = position du curseur à faire évoluer.               *
*                ctrl    = indique la demande d'un parcours rapide.           *
*                dir     = direction du parcours.                             *
*                display = règles d'affichage des colonnes modulables.        *
*                                                                             *
*  Description : Déplace le curseur au sein d'une vue de tampon.              *
*                                                                             *
*  Retour      : true si un déplacement a été effectué, false sinon.          *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool _g_buffer_view_move_caret(GBufferView *view, const GBufferLine *line, GdkRectangle *caret, bool ctrl, GdkScrollDirection dir, const bool *display)
{
    bool result;

    gint tmp_x;                             /* Copie de travail modifiable */
    GBufferSegment *segment;                /* Segment visé par le pointeur*/
    size_t first;                           /* Première ligne intégrée     */

    gint ref_x;


    gint offset;



    tmp_x = caret->x;

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

    segment = g_buffer_line_get_segment_at(line, view->max_widths, display, &tmp_x, dir, false);

    if (segment == NULL) printf(" ===== NO SEG...\n");

    if (segment == NULL) return false;


    printf(" ====== FIRST SEG :: %p ('%s')\n", segment, g_buffer_segment_get_text(segment));



    ref_x = tmp_x;



    //if (dir == GDK_SCROLL_LEFT || dir == GDK_SCROLL_RIGHT)
        result = g_buffer_segment_move_caret(segment, &tmp_x, ctrl, dir);
    //else
        //result = true;

    printf(" ====== MOVE 1 ? %d\n", result);


    if (!result)
    {


        segment = g_buffer_line_find_near_segment(line, segment, view->max_widths, display, dir, &offset);


        printf(" ====== NEAR SEG :: %p ('%s')\n", segment, segment ? g_buffer_segment_get_text(segment) : NULL);

        if (segment != NULL)
        {
            //tmp_x = /*ref_x + */g_buffer_segment_get_width(segment);


            //ref_x = tmp_x;
            ref_x = tmp_x = 0;


            ref_x = 0;
            tmp_x = offset;


            result = true;
            //result = g_buffer_segment_move_caret(segment, &tmp_x, ctrl, dir);

            /*
            if (result)
                caret->x -= COL_MARGIN;
            */

            printf(" ====== MOVE 2 ? %d (offset=%d)\n", result, offset);


        }


    }


    if (result)
        printf(" ====== NEW CARET: %d -> %d\n", caret->x, caret->x + (tmp_x - ref_x));
    else
        printf(" ====== NO NEW CARET!\n");



    if (result)
        caret->x += (tmp_x - ref_x);



    return result;


    //bool g_buffer_segment_move_caret(const GBufferSegment *segment, gint *x, bool ctrl, GdkScrollDirection dir)



    /* TODO : utiliser les arguments booléens */

    caret->x += (dir == GDK_SCROLL_RIGHT ? 10 : -10);

    return true;


    return false;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view    = vue de tampon à mettre à jour.                     *
*                caret   = position du curseur à faire évoluer.               *
*                ctrl    = indique la demande d'un parcours rapide.           *
*                dir     = direction du parcours.                             *
*                display = règles d'affichage des colonnes modulables.        *
*                                                                             *
*  Description : Déplace le curseur au sein d'une vue de tampon.              *
*                                                                             *
*  Retour      : Adresse si une a pu être déterminée, VMPA_INVALID sinon.     *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

const vmpa2t *g_buffer_view_move_caret(GBufferView *view, GdkRectangle *caret, bool ctrl, GdkScrollDirection dir, const bool *display)
{
    vmpa2t *result;                         /* Actualisation à renvoyer    */
    GBufferLine *line;                      /* Ligne sous le pointeur      */


    bool computed;                          /* Récursivité pris en compte  */
    gint lheight;                           /* Hauteur d'une ligne         */
    gint left_pos;                          /* Retour à la ligne           */
    gint right_pos;                         /* Position d'extrème droite   */
    BufferLineColumn i;                     /* Boucle de parcours          */
    size_t first;                           /* Première ligne intégrée     */
    size_t last;                            /* Dernière ligne intégrée     */

    size_t index;                           /* Indice de ligne de tampon   */


    bool moved;                             /* Mémorisation d'une évolut°  */






    gint tmp_x;                             /* Copie de travail modifiable */
    GBufferSegment *segment;                /* Segment visé par le pointeur*/



    result = NULL;
    computed = false;



    line = g_buffer_view_find_line_at(view, caret->y, &index);
    if (line == NULL) return NULL;


    lheight = g_buffer_view_get_line_height(view);


    switch (dir)
    {
        case GDK_SCROLL_UP:
        case GDK_SCROLL_DOWN:
            lheight = g_buffer_view_get_line_height(view);
            break;
        case GDK_SCROLL_LEFT:
        case GDK_SCROLL_RIGHT:
            left_pos = view->left_text;
            if (display[BLC_PHYSICAL]) left_pos += view->max_widths[BLC_PHYSICAL] + COL_MARGIN;
            if (display[BLC_VIRTUAL]) left_pos += view->max_widths[BLC_VIRTUAL] + COL_MARGIN;
            if (display[BLC_BINARY]) left_pos += view->max_widths[BLC_BINARY] + COL_MARGIN;
            right_pos = left_pos;
            for (i = BLC_ASSEMBLY_HEAD; i < BLC_COUNT; i++)
                right_pos += view->max_widths[i] + COL_MARGIN;



            left_pos = view->left_text;

            break;
        default:    /* GDK_SCROLL_SMOOTH */
            break;
    }

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

    first = 0;

    switch (dir)
    {
        case GDK_SCROLL_UP:

            if (index > first)
            {
                line = view->buffer->lines[index - 1];
                result = g_buffer_view_compute_caret(view, line, index - 1, caret->x, display, caret);
            }

            break;

        case GDK_SCROLL_DOWN:

            if (index < last)
            {
                line = view->buffer->lines[index + 1];
                result = g_buffer_view_compute_caret(view, line, index + 1, caret->x, display, caret);
            }

            break;

        case GDK_SCROLL_LEFT:

            line = g_buffer_view_find_line_at(view, caret->y, &index);
            if (line == NULL) break;

            moved = _g_buffer_view_move_caret(view, line, caret, ctrl, GDK_SCROLL_LEFT, display);

            if (!moved && index > first)
            {
                line = view->buffer->lines[index - 1];
                result = g_buffer_view_compute_caret(view, line, index - 1, INT_MAX, display, caret);
            }

            break;

        case GDK_SCROLL_RIGHT:

            line = g_buffer_view_find_line_at(view, caret->y, &index);
            if (line == NULL) break;

            moved = _g_buffer_view_move_caret(view, line, caret, ctrl, GDK_SCROLL_RIGHT, display);

            if (!moved && index < last)
            {
                line = view->buffer->lines[index + 1];
                result = g_buffer_view_compute_caret(view, line, index + 1, left_pos, display, caret);
            }

            break;

        default:    /* GDK_SCROLL_SMOOTH */
            break;

    }


    printf(" --- CARET ---   moved = %d   index = %d   result = %p\n",
           moved, index, result);



    /*
    if (result && !computed)
        result = g_buffer_view_compute_caret(view, caret->x, caret->y, caret, display, NULL);
    */

    return result;

}


/******************************************************************************
*                                                                             *
*  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)
{
#if 0   /* FIXME : inclusion des champs adresses/code */
    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, NULL);
    if (line == NULL) return;

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

    segment = g_buffer_line_get_segment_at(line, view->max_widths, &x, GDK_SCROLL_LEFT, false);
    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, true);
    last = g_code_buffer_get_index_from_address(view->buffer, view->end, false);

    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");
#endif
}


/******************************************************************************
*                                                                             *
*  Paramètres  : view    = visualisation à représenter.                       *
*                cr      = contexte graphique dédié à la procédure.           *
*                fake_x  = abscisse réelle du point 0 à l'écran.              *
*                fake_y  = ordonnée réelle du point 0 à l'écran.              *
*                area    = position et surface à traiter.                     *
*                display = règles d'affichage des colonnes modulables.        *
*                                                                             *
*  Description : Imprime la visualisation du tampon de code désassemblé.      *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_buffer_view_draw(const GBufferView *view, cairo_t *cr, gint fake_x, gint fake_y, const cairo_rectangle_int_t *area, const bool *display)
{
    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          */

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

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

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

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

    y = 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], cr, view->max_widths, real_x, y, display);

            y += view->line_height;

        }

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view    = visualisation à représenter.                       *
*                ctx     = éléments à disposition pour l'exportation.         *
*                type    = type d'exportation attendue.                       *
*                display = règles d'affichage des colonnes modulables.        *
*                                                                             *
*  Description : Exporte le contenu du tampon de code désassemblé.            *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_buffer_view_export(const GBufferView *view, buffer_export_context *ctx, BufferExportType type, const bool *display)
{
    size_t start;                           /* Première ligne visée        */
    size_t end;                             /* Dernière ligne avant limite */
    GBufferLine **lines;                    /* Liste des lignes à traiter  */
    size_t i;                               /* Boucle de parcours          */

    start = g_code_buffer_get_index_from_address(view->buffer, view->start, true);
    end = g_code_buffer_get_index_from_address(view->buffer, view->end, false);

    lines = view->buffer->lines;

    switch (type)
    {
        case BET_HTML:
            dprintf(ctx->fd, "<HTML>\n");
            dprintf(ctx->fd, "<HEAD>\n");
            dprintf(ctx->fd, "\t<META http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n");
            dprintf(ctx->fd, "</HEAD>\n");
            dprintf(ctx->fd, "<BODY>\n");
            dprintf(ctx->fd, "<STYLE  type=\"text/css\">\n");
            dprintf(ctx->fd, "TABLE {\n");
            dprintf(ctx->fd, "\tbackground-color: %s;\n", ctx->bg_color);
            dprintf(ctx->fd, "\tborder: 0px;\n");
            dprintf(ctx->fd, "\tfont-family: %s;\n", ctx->font_name);
            dprintf(ctx->fd, "}\n");
            dprintf(ctx->fd, "TD {\n");
            dprintf(ctx->fd, "\tborder: 0px;\n");
            dprintf(ctx->fd, "\tpadding-left: 8px;\n");
            dprintf(ctx->fd, "\tpadding-right: 8px;\n");
            dprintf(ctx->fd, "}\n");
            g_buffer_segment_export_style(ctx, type);
            dprintf(ctx->fd, "</STYLE>\n");
            dprintf(ctx->fd, "<TABLE>\n");
            break;
        default:
            break;
    }

    if (view->buffer->used > 0)
        for (i = start; i <= end; i++)
            g_buffer_line_export(lines[i], ctx, type, display);

    switch (type)
    {
        case BET_HTML:
            dprintf(ctx->fd, "</TABLE>\n");
            dprintf(ctx->fd, "</BODY>\n");
            dprintf(ctx->fd, "</HTML>\n");
            break;
        default:
            break;
    }

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view = visualisation à consulter.                            *
*                y    = ordonnée comprise dans la ligne recherchée.           *
*                idx  = indice de la ligne trouvée ou NULL. [OUT]             *
*                                                                             *
*  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, size_t *idx)
{
    gint lheight;                           /* Hauteur d'une ligne         */
    size_t index;                           /* Indice attendu              */

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

    if (idx != NULL)
        *idx = index;

    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, const vmpa2t *addr, gint *x, gint *y)
{
    bool result;                            /* Bilan à retourner           */
    gint lheight;                           /* Hauteur d'une ligne         */
    size_t first;                           /* Première ligne intégrée     */
    size_t last;                            /* Dernière ligne intégrée     */
    size_t i;                               /* Boucle de parcours          */
    const mrange_t *range;                  /* Emplacement parcouru        */

    result = false;

    *x = 0;
    *y = 0;

    lheight = g_buffer_view_get_line_height(view);

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

    if (view->buffer->used > 0)
        for (i = first; i <= last; i++)
        {
            /**
             * Si l'adresse recherchée est plus petite que l'adresse de départ,
             * on va effectuer un parcours complet pour rien.
             *
             * On considère cependant que le seul cas où celà peut arriver
             * est lorsque que des découpages en blocs sont impliqués.
             *
             * Les découpages conduisent alors à la formation de petites zones,
             * rapides à parcourir.
             */

            range = g_buffer_line_get_range(view->buffer->lines[i]);

            result = mrange_contains_addr(range, addr);
            if (result) break;

            *y += lheight;

        }

    return result;

}