/* Chrysalide - Outil d'analyse de fichiers binaires
 * gcodebuffer.c - 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 <assert.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>


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

    vmpa2t start;                           /* Début du parcours           */
    bool has_start;                         /* Validité des données #1     */
    vmpa2t end;                             /* Fin du parcours             */
    bool has_end;                           /* Validité des données #2     */

    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 *);

/* Supprime toutes les références externes. */
static void g_buffer_scan_dispose(GBufferScan *);

/* Procède à la libération totale de la mémoire. */
static void g_buffer_scan_finalize(GBufferScan *);

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

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



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


/* Suivi distant des évolutions */
typedef struct _view_callback
{
    buffer_size_changed_cb size_changed;    /* Evolution de taille         */
    GObject *data;                          /* Données à associer          */

} view_callback;


/* 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           */

    GWidthTracker *tracker;                 /* Suivi des largeurs          */

    size_t indent;                          /* Indentation des lignes      */

    view_callback *vcallbacks;              /* Vues à mettre à jour        */
    size_t vcount;                          /* Quantité de ces vues        */

};

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

    /* Signaux */

    void (* line_changed) (GCodeBuffer *, GBufferLine *, GBufferSegment *);

};


/* 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 *);

/* Réagit à un changement de contenu d'une ligne donnée. */
static void on_line_content_change(GBufferLine *, GBufferSegment *, GCodeBuffer *);

/* Réagit à un changement de propriété rattachée à une ligne. */
static void on_line_flag_flip(GBufferLine *, BufferLineFlags, BufferLineFlags, GCodeBuffer *);

/* Ajoute de nouvelles lignes à une position donnée. */
static void g_code_buffer_insert_lines_at(GCodeBuffer *, GBufferLine **, size_t, size_t);



/* ------------------------- CONFORTS POUR LES COMMENTAIRES ------------------------- */


/* Affiche un commentaire sur une ligne de tampon donnée. */
static bool _g_code_buffer_write_inlined_comment(GCodeBuffer *, GBufferLine *, const char *, GObject *);

/* Affiche un commentaire sur une ligne de tampon dédiée. */
static bool _g_code_buffer_write_comment_area(GCodeBuffer *, GBufferLine *, const char *, bool, GObject *);

/* Retrouve la première ligne d'une zone de commentaire. */
static size_t g_code_buffer_find_first_line_comment(const GCodeBuffer *, size_t);

/* Retrouve la dernière ligne d'une zone de commentaire. */
static size_t g_code_buffer_find_last_line_comment(const GCodeBuffer *, size_t);

/* Supprime un commentaire existant. */
static bool _g_code_buffer_delete_lines_comment(GCodeBuffer *, GBufferLine *);



/* ------------------------- SIGNAUX IMMEDIATS POUR UNE VUE ------------------------- */


/* Fait suivre une variation de la quantité de lignes du tampon. */
static void g_code_buffer_notify_size_changed(const GCodeBuffer *, bool, size_t, size_t);



/* ---------------------------------------------------------------------------------- */
/*                            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)
{
    GObjectClass *object;                   /* Autre version de la classe  */
    GDelayedWorkClass *work;                /* Version en classe parente   */

    object = G_OBJECT_CLASS(klass);

    object->dispose = (GObjectFinalizeFunc/* ! */)g_buffer_scan_dispose;
    object->finalize = (GObjectFinalizeFunc)g_buffer_scan_finalize;

    work = G_DELAYED_WORK_CLASS(klass);

    work->run = (run_task_fc)g_buffer_scan_process;

}


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

static void g_buffer_scan_init(GBufferScan *scan)
{

}


/******************************************************************************
*                                                                             *
*  Paramètres  : scan = instance d'objet GLib à traiter.                      *
*                                                                             *
*  Description : Supprime toutes les références externes.                     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_buffer_scan_dispose(GBufferScan *scan)
{
    g_object_unref(G_OBJECT(scan->buffer));

    G_OBJECT_CLASS(g_buffer_scan_parent_class)->dispose(G_OBJECT(scan));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : scan = instance d'objet GLib à traiter.                      *
*                                                                             *
*  Description : Procède à la libération totale de la mémoire.                *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_buffer_scan_finalize(GBufferScan *scan)
{
    free(scan->message);

    G_OBJECT_CLASS(g_buffer_scan_parent_class)->finalize(G_OBJECT(scan));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer  = tampon à manipuler.                                *
*                start   = première adresse visée ou NULL.                    *
*                end     = dernière adresse visée ou NULL.                    *
*                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, const vmpa2t *start, const vmpa2t *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->has_start = (start != NULL);

    if (result->has_start)
        copy_vmpa(&result->start, start);

    result->has_end = (end != NULL);

    if (result->has_end)
        copy_vmpa(&result->end, end);

    result->message = strdup(message);

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

    return result;

}


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

static void g_buffer_scan_process(GBufferScan *scan, GtkStatusStack *status)
{
    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 */

    if (scan->has_start)
        first = g_code_buffer_get_index_from_address(scan->buffer, &scan->start, true);
    else
        first = 0;

    if (scan->has_end)
        last = g_code_buffer_get_index_from_address(scan->buffer, &scan->end, false);
    else
        last = scan->buffer->used - 1;

    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);

}



/* ---------------------------------------------------------------------------------- */
/*                            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)
{
    g_signal_new("line-changed",
                 G_TYPE_CODE_BUFFER,
                 G_SIGNAL_RUN_LAST,
                 G_STRUCT_OFFSET(GCodeBufferClass, line_changed),
                 NULL, NULL,
                 g_cclosure_user_marshal_VOID__OBJECT_OBJECT,
                 G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_OBJECT);

}


/******************************************************************************
*                                                                             *
*  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)
{
    buffer->tracker = g_width_tracker_new(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 GLib à consulter.                         *
*                                                                             *
*  Description : Compte le nombre de lignes rassemblées dans un tampon.       *
*                                                                             *
*  Retour      : Nombre de lignes constituant le tampon.                      *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

size_t g_code_buffer_count_lines(const GCodeBuffer *buffer)
{
    return buffer->used;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = composant GLib à consulter.                         *
*                                                                             *
*  Description : Fournit un lien vers la structure de suivi de largeurs.      *
*                                                                             *
*  Retour      : Gestionnaire de largeurs de lignes.                          *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

const GWidthTracker *g_code_buffer_get_width_tracker(const GCodeBuffer *buffer)
{
    return buffer->tracker;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = composant GLib à consulter.                         *
*                range  = emplacement où va se situer la ligne.               *
*                                                                             *
*  Description : Initie une nouvelle ligne devant être insérée dans le tampon.*
*                                                                             *
*  Retour      : Nouvelle ligne vierge à écrire.                              *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GBufferLine *g_code_buffer_prepare_new_line(GCodeBuffer *buffer, const mrange_t *range)
{
    GBufferLine *result;                    /* Instance à retourner        */
    size_t i;                               /* Boucle de parcours          */

    result = g_buffer_line_new(range, buffer->main_column);

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

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : line    = ligne dont la définition vient d'évoluer.          *
*                segment = éventuel segment qui vient d'évoluer ou NULL.      *
*                buffer  = tampon de lignes cohérentes à manipuler.           *
*                                                                             *
*  Description : Réagit à un changement de contenu d'une ligne donnée.        *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void on_line_content_change(GBufferLine *line, GBufferSegment *segment, GCodeBuffer *buffer)
{
    g_signal_emit_by_name(buffer, "line-changed", line, segment);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : line   = ligne dont la définition vient d'évoluer.           *
*                old    = ancien groupe de propriétés associées.              *
*                old    = nouveau groupe de propriétés associées.             *
*                buffer = tampon de lignes cohérentes à manipuler.            *
*                                                                             *
*  Description : Réagit à un changement de propriété rattachée à une ligne.   *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void on_line_flag_flip(GBufferLine *line, BufferLineFlags old, BufferLineFlags new, GCodeBuffer *buffer)
{
    g_signal_emit_by_name(buffer, "line-changed", line, NULL);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = composant GLib à mettre à jour.                     *
*                lines  = liste de lignes à insérer.                          *
*                count  = taille de cette liste.                              *
*                index  = point d'insertion de la première ligne.             *
*                                                                             *
*  Description : Ajoute de nouvelles lignes à une position donnée.            *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_code_buffer_insert_lines_at(GCodeBuffer *buffer, GBufferLine **lines, size_t count, size_t index)
{
    size_t i;                               /* Boucle de parcours          */

    /* Elaboration d'un espace suffisant */

    if ((buffer->used + count) > buffer->count)
    {
        if (count > LINE_ALLOC_BULK)
            buffer->count += count;
        else
            buffer->count += LINE_ALLOC_BULK;

        buffer->lines = (GBufferLine **)realloc(buffer->lines,
                                                buffer->count * sizeof(GBufferLine *));

    }

    /* Insertion des lignes */

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

    buffer->used += count;

    for (i = 0; i < count; i++)
    {
        assert((index + i) < buffer->used);

        buffer->lines[index + i] = lines[i];

        g_signal_connect(lines[i], "content-changed", G_CALLBACK(on_line_content_change), buffer);
        g_signal_connect(lines[i], "flip-flag", G_CALLBACK(on_line_flag_flip), buffer);

    }

    /* Recueil initial des largeurs */

    g_width_tracker_update_added(buffer->tracker, index, count);

    g_code_buffer_notify_size_changed(buffer, true, index, count);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = composant GLib à mettre à jour.                     *
*                line   = lign à insérer à la fin du tampon.                  *
*                                                                             *
*  Description : Ajoute une nouvelle ligne en fin de tampon.                  *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_code_buffer_append_new_line(GCodeBuffer *buffer, GBufferLine *line)
{
    g_code_buffer_insert_lines_at(buffer, (GBufferLine *[]) { line }, 1, buffer->used);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = composant GLib à mettre à jour.                     *
*                lines  = liste de lignes à insérer.                          *
*                count  = taille de cette liste.                              *
*                point  = point d'insertion du bloc de ligne.                 *
*                before = emplacement de l'insertion par rapport au point.    *
*                                                                             *
*  Description : Ajoute de nouvelles lignes par rapport à une ligne donnée.   *
*                                                                             *
*  Retour      : Bilan : insertion réussie ou non ?                           *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool g_code_buffer_insert_lines(GCodeBuffer *buffer, GBufferLine **lines, size_t count, const GBufferLine *point, bool before)
{
    bool result;                            /* Bilan à retourner           */
    size_t index;                           /* Indice d'insertion final    */

    result = false;

    index = g_code_buffer_find_index_by_line(buffer, point);

    if (index == buffer->used)
        goto gcbil_exit;

    if (!before)
        index++;

    g_code_buffer_insert_lines_at(buffer, lines, count, index);




    result = true;

 gcbil_exit:

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = composant GLib à mettre à jour.                     *
*                start  = première ligne devant être supprimée.               *
*                end    = dernière ligne devant être supprimée.               *
*                                                                             *
*  Description : Supprime une ou plusieurs lignes du tampon indiqué.          *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_code_buffer_delete_lines(GCodeBuffer *buffer, size_t start, size_t end)
{
    size_t i;                               /* Boucle de parcours          */
    GBufferLine *line;                      /* Ligne en cours de traitement*/

    assert(start < buffer->used);
    assert(end < buffer->used);

    for (i = start; i <= end; i++)
    {
        line = buffer->lines[i];

        g_signal_handlers_disconnect_by_func(line, G_CALLBACK(on_line_content_change), buffer);
        g_signal_handlers_disconnect_by_func(line, G_CALLBACK(on_line_flag_flip), buffer);

        g_object_unref(G_OBJECT(line));

    }

    if ((end + 1) < buffer->used)
        memmove(&buffer->lines[start], &buffer->lines[end + 1],
                (buffer->used - end - 1) * sizeof(GBufferLine *));

    buffer->used -= (end - start + 1);

    g_width_tracker_update_deleted(buffer->tracker, start, end);

    g_code_buffer_notify_size_changed(buffer, false, start, end - start + 1);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = composant GTK à mettre à jour.                      *
*                addr   = adresse où retrouver la ligne recherchée.           *
*                flags  = propriétés à vérifier en tout ou partie.            *
*                idx    = indice de la ligne trouvée ou NULL. [OUT]           *
*                                                                             *
*  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, const vmpa2t *addr, BufferLineFlags flags, size_t *idx)
{
    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
    {
        if (idx != NULL)
            *idx = index;

        result = buffer->lines[index];

        if (flags != BLF_NONE)
            while ((g_buffer_line_get_flags(result) & flags) != flags)
            {
                if ((index + 1) == buffer->used) break;

                /* FIXME : vérifier que l'adresse est toujours celle recherchée ! */

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

                result = buffer->lines[++index];

            }

        g_object_ref(G_OBJECT(result));

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = tampon de lignes à consulter.                       *
*                index  = indice de la ligne recherchée.                      *
*                                                                             *
*  Description : Retrouve une ligne au sein d'un tampon avec un indice.       *
*                                                                             *
*  Retour      : Line retrouvée ou NULL en cas d'échec.                       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GBufferLine *g_code_buffer_find_line_by_index(const GCodeBuffer *buffer, size_t index)
{
    GBufferLine *result;                    /* Ligne trouvée à retourner   */

    /* TODO : ref */

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

    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 le nombre de lignes sinon.   *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

size_t g_code_buffer_get_index_from_address(const GCodeBuffer *buffer, const vmpa2t *addr, bool first)
{
    size_t result;                          /* Indice à retourner          */
    GBufferLine **found;                    /* Renvoi vers une trouvaille  */
    const mrange_t *range;                  /* Couverture d'une ligne      */

    /**
     * Si aucune adresse (ie. aucune limite ?) n'est précisée, on se base sur
     * la direction pour trouver le bon indice.
     */

    if (addr == NULL)
        result = (first ? 0 : buffer->used - 1);

    /**
     * Sinon on parcourt méthodiquement toutes les lignes !
     */

    else
    {
        /* Recherche dichotomique grossière */

        int cmp_addr_and_line(const vmpa2t *addr, const GBufferLine **line)
        {
            int status;                     /* Bilan d'une comparaison     */
            const mrange_t *lrange;         /* Couverture d'une ligne      */

            lrange = g_buffer_line_get_range(*line);

            status = cmp_mrange_with_vmpa(lrange, addr);

            return status;

        }

        found = bsearch(addr, buffer->lines, buffer->used, sizeof(GBufferLine *),
                        (__compar_fn_t)cmp_addr_and_line);

        /* Dernier raffinage pour approcher la cible réelle */

        if (found == NULL)
            result = buffer->used;

        else
        {
            result = found - buffer->lines;

            if (first)
                for (; result > 0; result--)
                {
                    range = g_buffer_line_get_range(buffer->lines[result - 1]);
                    if (!mrange_contains_addr(range, addr)) break;
                }

            else
                for (; (result + 1) < buffer->used; result++)
                {
                    range = g_buffer_line_get_range(buffer->lines[result + 1]);
                    if (!mrange_contains_addr(range, addr)) break;
                }

        }

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = tampon de lignes à consulter.                       *
*                line   = ligne dont l'indice est à retrouver.                *
*                                                                             *
*  Description : Retrouve l'indice associé à une ligne au sein d'un tampon.   *
*                                                                             *
*  Retour      : Indice de l'adresse trouvée, ou le nombre de lignes sinon.   *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

size_t g_code_buffer_find_index_by_line(const GCodeBuffer *buffer, const GBufferLine *line)
{
    size_t result;                          /* Indice trouvé à retourner   */
    const mrange_t *range;                  /* Emplacement de la ligne     */
    const mrange_t *next;                   /* Emplacement suivant         */

    range = g_buffer_line_get_range(line);

    result = g_code_buffer_get_index_from_address(buffer, get_mrange_addr(range), true);

    /**
     * Comme plusieurs lignes consécutives peuvent avoir la même adresse,
     * on parcourt les lignes suivantes pour retrouver la ligne recherchée.
     */

    if (result < buffer->used)
    {
        while (buffer->lines[result] != line)
        {
            if (++result == buffer->used)
                break;

            next = g_buffer_line_get_range(buffer->lines[result]);

            if (cmp_vmpa(get_mrange_addr(range), get_mrange_addr(next)) != 0)
            {
                result = buffer->used;
                break;
            }

        }

    }

    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   : -                                                            *
*                                                                             *
******************************************************************************/

GDelayedWork *g_buffer_code_scan(GCodeBuffer *buffer, const vmpa2t *start, const vmpa2t *end, const char *message, process_line_fc process, void *data)
{
    GBufferScan *result;                    /* Procédure à créer / renvoyer*/
    GWorkQueue *queue;                      /* Gestionnaire de différés    */

    result = g_buffer_scan_new(buffer, start, end, message, process, data);
    g_object_ref(G_OBJECT(result));

    queue = get_work_queue();
    g_work_queue_schedule_work(queue, G_DELAYED_WORK(result), DEFAULT_WORK_GROUP);

    return G_DELAYED_WORK(result);

}



/* ---------------------------------------------------------------------------------- */
/*                           CONFORTS POUR LES COMMENTAIRES                           */
/* ---------------------------------------------------------------------------------- */


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer  = tampon de lignes à consulter.                      *
*                line    = ligne à l'intérieur d'un commentaire.              *
*                comment = nouveau commentaire à inscrire à la ligne donnée.  *
*                creator = créateur à l'origine de la construction.           *
*                                                                             *
*  Description : Affiche un commentaire sur une ligne de tampon donnée.       *
*                                                                             *
*  Retour      : Bilan de l'opération : ajout ou non ?                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool _g_code_buffer_write_inlined_comment(GCodeBuffer *buffer, GBufferLine *line, const char *comment, GObject *creator)
{
    bool result;                            /* Bilan à retourner           */
    const mrange_t *range;                  /* Emplace de ligne à utiliser */
    char *wcopy;                            /* Copie de travail            */
    GBufferLine **extra;                    /* Lignes supplémentaires      */
    size_t extra_count;                     /* Quantité de ces lignes      */
    char *saveptr;                          /* Sauvegarde pour la sécurité */
    char *token;                            /* Fragment à insérer          */
    size_t len;                             /* Taille dudit fragment       */
    GBufferSegment *segment;                /* Segment à marquer au fer    */
    GBufferLine *new;                       /* Nouvelle ligne créée        */
    size_t i;                               /* Boucle de parcours          */

    assert(!g_buffer_line_has_comment(line));

    result = false;

    range = g_buffer_line_get_range(line);

    wcopy = strdup(comment);

    extra = NULL;
    extra_count = 0;

    for (token = strtok_r(wcopy, COMMENT_LINE_SEP, &saveptr);
         token != NULL;
         token = strtok_r(NULL, COMMENT_LINE_SEP, &saveptr))
    {
        len = strlen(token);

        if (!result)
        {
            segment = g_buffer_line_insert_text(line, BLC_COMMENTS, token, len, RTT_COMMENT);
            g_buffer_segment_set_creator(segment, creator);
        }

        else
        {
            new = g_code_buffer_prepare_new_line(buffer, range);

            segment = g_buffer_line_insert_text(new, BLC_COMMENTS, token, len, RTT_COMMENT);
            g_buffer_segment_set_creator(segment, creator);

            extra = (GBufferLine **)realloc(extra, ++extra_count * sizeof(GBufferLine *));

            extra[extra_count - 1] = new;

        }

        result = true;

    }

    free(wcopy);

    if (extra_count > 0)
    {
        result &= g_code_buffer_insert_lines(buffer, extra, extra_count, line, false);

        if (!result)
            for (i = 0; i < extra_count; i++)
                g_object_unref(G_OBJECT(extra[i]));

        free(extra);

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer  = tampon de lignes à consulter.                      *
*                line    = ligne à l'intérieur d'un commentaire.              *
*                comment = nouveau commentaire à inscrire à la ligne donnée.  *
*                creator = créateur à l'origine de la construction.           *
*                                                                             *
*  Description : Affiche un commentaire sur une ligne de tampon donnée.       *
*                                                                             *
*  Retour      : Bilan de l'opération : ajout ou non ?                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool g_code_buffer_update_inlined_comment(GCodeBuffer *buffer, GBufferLine *line, const char *comment, GObject *creator)
{
    bool result;                            /* Bilan à retourner           */

    if (g_buffer_line_has_comment(line))
        result = _g_code_buffer_delete_lines_comment(buffer, line);
    else
        result = true;

    if (result)
        result = _g_code_buffer_write_inlined_comment(buffer, line, comment, creator);

    /* TODO : emit() */

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer  = tampon de lignes à consulter.                      *
*                line    = ligne à l'intérieur d'un commentaire.              *
*                comment = nouveau commentaire à inscrire à la ligne donnée.  *
*                before  = précise la position du commentaire.                *
*                creator = créateur à l'origine de la construction.           *
*                                                                             *
*  Description : Affiche un commentaire sur une ligne de tampon dédiée.       *
*                                                                             *
*  Retour      : Bilan de l'opération : ajout ou non ?                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool _g_code_buffer_write_comment_area(GCodeBuffer *buffer, GBufferLine *line, const char *comment, bool before, GObject *creator)
{
    bool result;                            /* Bilan à retourner           */
    const mrange_t *range;                  /* Emplace de ligne à utiliser */
    char *wcopy;                            /* Copie de travail            */
    GBufferLine **extra;                    /* Lignes supplémentaires      */
    size_t extra_count;                     /* Quantité de ces lignes      */
    char *saveptr;                          /* Sauvegarde pour la sécurité */
    char *token;                            /* Fragment à insérer          */
    size_t len;                             /* Taille dudit fragment       */
    GBufferLine *new;                       /* Nouvelle ligne créée        */
    GBufferSegment *segment;                /* Segment à marquer au fer    */
    size_t i;                               /* Boucle de parcours          */

    assert(!g_buffer_line_has_comment(line));

    result = false;

    range = g_buffer_line_get_range(line);

    wcopy = strdup(comment);

    extra = NULL;
    extra_count = 0;

    for (token = strtok_r(wcopy, COMMENT_LINE_SEP, &saveptr);
         token != NULL;
         token = strtok_r(NULL, COMMENT_LINE_SEP, &saveptr))
    {
        len = strlen(token);

        new = g_code_buffer_prepare_new_line(buffer, range);
        g_buffer_line_start_merge_at(new, BLC_DISPLAY);

        segment = g_buffer_line_insert_text(new, BLC_DISPLAY, token, len, RTT_COMMENT);
        g_buffer_segment_set_creator(segment, creator);

        extra = (GBufferLine **)realloc(extra, ++extra_count * sizeof(GBufferLine *));

        extra[extra_count - 1] = new;

        result = true;

    }

    free(wcopy);

    if (extra_count > 0)
    {
        result &= g_code_buffer_insert_lines(buffer, extra, extra_count, line, before);

        if (!result)
            for (i = 0; i < extra_count; i++)
                g_object_unref(G_OBJECT(extra[i]));

        free(extra);

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer  = tampon de lignes à consulter.                      *
*                line    = ligne à l'intérieur d'un commentaire.              *
*                comment = nouveau commentaire à inscrire à la ligne donnée.  *
*                before  = précise la position du commentaire.                *
*                creator = créateur à l'origine de la construction.           *
*                                                                             *
*  Description : Affiche un commentaire sur une ligne de tampon dédiée.       *
*                                                                             *
*  Retour      : Bilan de l'opération : ajout ou non ?                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool g_code_buffer_update_comment_area(GCodeBuffer *buffer, GBufferLine *line, const char *comment, bool before, GObject *creator)
{
    bool result;                            /* Bilan à retourner           */

    if (g_buffer_line_has_comment(line))
        result = _g_code_buffer_delete_lines_comment(buffer, line);
    else
        result = true;

    if (result)
        result = _g_code_buffer_write_comment_area(buffer, line, comment, before, creator);

    /* TODO : emit() */

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = tampon de lignes à consulter.                       *
*                index  = indice de ligne à l'intérieur d'un commentaire.     *
*                                                                             *
*  Description : Retrouve la première ligne d'une zone de commentaire.        *
*                                                                             *
*  Retour      : Indice de l'adresse trouvée, ou le nombre de lignes sinon.   *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static size_t g_code_buffer_find_first_line_comment(const GCodeBuffer *buffer, size_t index)
{
    size_t result;                          /* Indice trouvé à retourner   */
    GBufferLine *prev;                      /* Ligne précédente            */

    assert(index < buffer->used);

    bool is_first_line_of_comment(const GBufferLine *ln, const GBufferLine *pv)
    {
        bool first;                         /* Statut à renvoyer           */
        BufferLineColumn merge_col;         /* Colonne de fusion #1        */
        BufferLineColumn prev_merge_col;    /* Colonne de fusion #2        */

        merge_col = g_buffer_line_get_merge_start(ln);

        /**
         * La ligne consultée contient toujours un commentaire.
         *
         * Deux cas de figures sont possibles ici :
         *
         *  - soit du texte est présent dans la colonne "commentaires".
         *    Si du texte est présent avant, alors il s'agit forcément de
         *    la première (et unique ?) ligne de commentaire.
         *
         *  - soit la ligne effectue une fusion des colonnes depuis BLC_DISPLAY.
         *    Si la ligne qui précède fait de même, il s'agit alors d'une étiquette
         *    ou de l'amont du commentaire.
         *
         */

        if (g_buffer_line_has_text(ln, BLC_COMMENTS, BLC_COUNT))
        {
            first = g_buffer_line_has_text(ln, BLC_DISPLAY, BLC_COMMENTS);

            if (!first)
            {
                /* La ligne d'avant doit avoir un commentaire ! */
                first = !g_buffer_line_has_text(pv, BLC_COMMENTS, BLC_COUNT);
            }

        }

        else
        {
            /**
             * Le prologue "merge_col == BLC_FIRST" n'étant pas éditable,
             * la seule fusion possible ici est la suivante.
             */
            assert(merge_col == BLC_DISPLAY);

            /**
             * La première ligne d'un tampon est toujours un prologue.
             */
            assert(pv != NULL);

            prev_merge_col = g_buffer_line_get_merge_start(pv);

            first = (prev_merge_col != BLC_DISPLAY);

            if (!first)
                first = (g_buffer_line_get_flags(pv) & BLF_IS_LABEL);

        }

        return first;

    }

    for (result = index; result > 0; result--)
    {
        prev = (result > 0 ? buffer->lines[result - 1] : NULL);

        if (is_first_line_of_comment(buffer->lines[result], prev))
            break;

    }

    if (result == 0)
    {
        if (!is_first_line_of_comment(buffer->lines[0], NULL))
            result = buffer->used;
    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = tampon de lignes à consulter.                       *
*                index  = indice de ligne à l'intérieur d'un commentaire.     *
*                                                                             *
*  Description : Retrouve la dernière ligne d'une zone de commentaire.        *
*                                                                             *
*  Retour      : Indice de l'adresse trouvée, ou le nombre de lignes sinon.   *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static size_t g_code_buffer_find_last_line_comment(const GCodeBuffer *buffer, size_t index)
{
    size_t result;                          /* Indice trouvé à retourner   */
    GBufferLine *next;                      /* Ligne suivante              */

    assert(index < buffer->used);

    bool is_last_line_of_comment(const GBufferLine *ln, const GBufferLine *nx)
    {
        bool last;                          /* Statut à renvoyer           */
        BufferLineColumn merge_col;         /* Colonne de fusion #1        */
        BufferLineColumn next_merge_col;    /* Colonne de fusion #2        */

        merge_col = g_buffer_line_get_merge_start(ln);

        /**
         * La ligne consultée contient toujours un commentaire.
         *
         * Deux cas de figures sont possibles ici :
         *
         *  - soit du texte est présent dans la colonne "commentaires".
         *    Si la ligne suivante est similaire et si du texte est présent avant,
         *    alors il s'agit forcément de d'un nouveau commentaire. S'il n'y a
         *    aucun texte, il s'agit de la suite du commentaire.
         *
         *  - soit la ligne effectue une fusion des colonnes depuis BLC_DISPLAY.
         *    Si la ligne qui suit fait de même, il s'agit alors d'une étiquette
         *    ou de l'aval du commentaire.
         *
         */

        if (g_buffer_line_has_text(ln, BLC_COMMENTS, BLC_COUNT))
        {
            last = !g_buffer_line_has_text(nx, BLC_COMMENTS, BLC_COUNT);

            if (!last)
                last = g_buffer_line_has_text(nx, BLC_DISPLAY, BLC_COMMENTS);

        }

        else
        {
            /**
             * Le prologue "merge_col == BLC_FIRST" n'étant pas éditable,
             * la seule fusion possible ici est la suivante.
             */
            assert(merge_col == BLC_DISPLAY);

            if (nx == NULL)
                last = true;

            else
            {
                next_merge_col = g_buffer_line_get_merge_start(nx);

                last = (next_merge_col != BLC_DISPLAY);

                if (!last)
                    last = (g_buffer_line_get_flags(nx) & BLF_IS_LABEL);

            }

        }

        return last;

    }

    for (result = index; result < buffer->used; result++)
    {
        next = ((result + 1) < buffer->used ? buffer->lines[result + 1] : NULL);

        if (is_last_line_of_comment(buffer->lines[result], next))
            break;

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = tampon de lignes à consulter.                       *
*                line   = ligne à l'intérieur d'un commentaire.               *
*                                                                             *
*  Description : Retrouve le créateur d'un commentaire existant.              *
*                                                                             *
*  Retour      : Instance trouvée à déréférencer ensuite ou NULL si aucune.   *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GObject *g_code_buffer_get_comment_creator(const GCodeBuffer *buffer, const GBufferLine *line)
{
    GObject *result;                        /* Instance à retourner        */
    BufferLineColumn merge_col;             /* Colonne de fusion           */

    if (g_buffer_line_has_comment(line))
    {
        merge_col = g_buffer_line_get_merge_start(line);

        if (merge_col == BLC_DISPLAY)
            result = g_buffer_line_find_first_segment_creator(line, BLC_DISPLAY);
        else
            result = g_buffer_line_find_first_segment_creator(line, BLC_COMMENTS);

    }

    else
        result = NULL;

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = tampon de lignes à consulter.                       *
*                line   = ligne à l'intérieur d'un commentaire.               *
*                                                                             *
*  Description : Récupère le contenu d'un commentaire existant.               *
*                                                                             *
*  Retour      : Commentaire retrouver à libérer ou NULL en cas d'échec.      *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

char *g_code_buffer_get_lines_comment(const GCodeBuffer *buffer, const GBufferLine *line)
{
    char *result;                           /* Contenu à retourner         */
    size_t index;                           /* Indice de la ligne fournie  */
    size_t start;                           /* Ligne de départ             */
    size_t end;                             /* Ligne d'arrivée             */
    BufferLineColumn merge_col;             /* Colonne de fusion           */
    size_t i;                               /* Boucle de parcours          */
    char *extra;                            /* Commentaire supplémentaire  */

    /* Pas de prologue ici ! */
    assert(g_buffer_line_has_comment(line));

    result = NULL;

    index = g_code_buffer_find_index_by_line(buffer, line);

    if (index == buffer->used)
        goto gcbglc_exit;

    start = g_code_buffer_find_first_line_comment(buffer, index);

    if (start == buffer->used)
        goto gcbglc_exit;

    end = g_code_buffer_find_last_line_comment(buffer, index);

    if (end == buffer->used)
        goto gcbglc_exit;

    merge_col = g_buffer_line_get_merge_start(line);

    for (i = start; i <= end; i++)
    {
        if (merge_col == BLC_DISPLAY)
            extra = g_buffer_line_get_text(buffer->lines[i], BLC_DISPLAY, BLC_COUNT, false);

        else
            extra = g_buffer_line_get_text(buffer->lines[i], BLC_COMMENTS, BLC_COUNT, false);

        assert(extra != NULL);

        if (result == NULL)
            result = extra;

        else
        {
            result = stradd(result, extra);
            free(extra);
        }

    }

 gcbglc_exit:

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = tampon de lignes à modifier.                        *
*                line   = ligne à l'intérieur d'un commentaire.               *
*                                                                             *
*  Description : Supprime un commentaire existant.                            *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool _g_code_buffer_delete_lines_comment(GCodeBuffer *buffer, GBufferLine *line)
{
    bool result;                            /* Bilan à retourner           */
    size_t index;                           /* Indice de la ligne fournie  */
    size_t start;                           /* Ligne de départ             */
    size_t end;                             /* Ligne d'arrivée             */
    BufferLineColumn merge_col;             /* Colonne de fusion           */

    /* Pas de prologue ici ! */
    assert(g_buffer_line_has_comment(line));

    result = false;

    index = g_code_buffer_find_index_by_line(buffer, line);

    if (index == buffer->used)
        goto gcbdlc_exit;

    start = g_code_buffer_find_first_line_comment(buffer, index);

    if (start == buffer->used)
        goto gcbdlc_exit;

    end = g_code_buffer_find_last_line_comment(buffer, index);

    if (end == buffer->used)
        goto gcbdlc_exit;

    result = true;

    merge_col = g_buffer_line_get_merge_start(line);

    if (merge_col == BLC_DISPLAY)
        g_code_buffer_delete_lines(buffer, start, end);

    else
    {
        g_buffer_line_delete_text(buffer->lines[start], BLC_COMMENTS, BLC_COUNT);

        if (end > start)
            g_code_buffer_delete_lines(buffer, start + 1, end);

    }

 gcbdlc_exit:

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = tampon de lignes à modifier.                        *
*                line   = ligne à l'intérieur d'un commentaire.               *
*                                                                             *
*  Description : Supprime un commentaire existant.                            *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool g_code_buffer_delete_lines_comment(GCodeBuffer *buffer, GBufferLine *line)
{
    bool result;                            /* Bilan à retourner           */

    result = _g_code_buffer_delete_lines_comment(buffer, line);

    /* TODO : emit() */

    return result;

}


/* ---------------------------------------------------------------------------------- */
/*                           SIGNAUX IMMEDIATS POUR UNE VUE                           */
/* ---------------------------------------------------------------------------------- */


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = tampon de lignes à modifier.                        *
*                cb     = fonction à appeler au moment opportun.              *
*                data   = object GLib à associer à l'appel.                   *
*                                                                             *
*  Description : Enregistre l'adresse d'une fonction de mise à jour de vue.   *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_code_buffer_register_view_callback(GCodeBuffer *buffer, buffer_size_changed_cb cb, GObject *data)
{
    view_callback *new;                     /* Informations sur l'appel    */

    buffer->vcount++;

    buffer->vcallbacks = (view_callback *)realloc(buffer->vcallbacks, buffer->vcount * sizeof(view_callback));

    new = &buffer->vcallbacks[buffer->vcount - 1];

    new->size_changed = cb;
    new->data = data;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = tampon de lignes à modifier.                        *
*                data   = object GLib à associer à l'appel.                   *
*                                                                             *
*  Description : Supprime un élément des vues à contacter pour mises à jour.  *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_code_buffer_unregister_view_callback(GCodeBuffer *buffer, GObject *data)
{
    size_t i;                               /* Boucle de parcours          */

    for (i = 0; i < buffer->vcount; i++)
        if (buffer->vcallbacks[i].data == data)
        {
            if ((i + 1) < buffer->vcount)
                memmove(&buffer->vcallbacks[i], &buffer->vcallbacks[i + 1],
                        (buffer->vcount - i - 1) * sizeof(view_callback));

            buffer->vcount--;

            buffer->vcallbacks = (view_callback *)realloc(buffer->vcallbacks,
                                                          buffer->vcount * sizeof(view_callback));

        }

}


/******************************************************************************
*                                                                             *
*  Paramètres  : buffer = tampon de lignes à diffuser.                        *
*                added  = indication sur la variation de la taille du tampon. *
*                index  = indice de la première ligne à traiter.              *
*                count  = nombre de lignes à traiter.                         *
*                                                                             *
*  Description : Fait suivre une variation de la quantité de lignes du tampon.*
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_code_buffer_notify_size_changed(const GCodeBuffer *buffer, bool added, size_t index, size_t count)
{
    size_t i;                               /* Boucle de parcours          */
    view_callback *cb;                      /* Informations sur l'appel    */

    for (i = 0; i < buffer->vcount; i++)
    {
        cb = &buffer->vcallbacks[i];

        cb->size_changed(buffer, added, index, count, cb->data);

    }

}