/* OpenIDA - Outil d'analyse de fichiers binaires
 * gbuffersegment.c - concentration d'un fragment de caractères aux propriétés communes
 *
 * Copyright (C) 2010 Cyrille Bagard
 *
 *  This file is part of OpenIDA.
 *
 *  OpenIDA is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  OpenIDA is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Foobar.  If not, see .
 */
#include "gbuffersegment.h"
#include 
/* Fragment de caractères aux propriétés communes (instance) */
struct _GBufferSegment
{
    GObject parent;                         /* A laisser en premier        */
    PangoGlyphString *glyphs;               /* Caractères traités          */
    PangoFont *font;                        /* Police utilisée à l'analyse */
    PangoRectangle logical;                 /* Dimension du texte          */
};
/* Fragment de caractères aux propriétés communes (classe) */
struct _GBufferSegmentClass
{
    GObjectClass parent;                    /* A laisser en premier        */
    PangoGlyphString *ascii_glyphs;         /* Caractères ASCII prêts      */
    PangoFont *ascii_font;                  /* Police utilisée pour ASCII  */
    bool ascii_ready;                       /* Utilisation possible ?      */
    bool ascii_init_done;                   /* Initialisation tentée ?     */
};
/* Procède à l'initialisation d'une classe de fragment de texte. */
static void g_buffer_segment_class_init(GBufferSegmentClass *);
/* Procède à l'initialisation d'un fragment de texte. */
static void g_buffer_segment_init(GBufferSegment *);
static bool ascii_glyph_table_init(GBufferSegmentClass *class, PangoContext *context)
{
    gint i;                                 /* Boucle de parcours          */
    char ascii_chars[128];                  /* Table de caractères ASCII   */
    PangoAttrList *attribs;                 /* Liste d'attributs (vide)    */
    GList *list;                            /* Liste d'éléments distincts  */
    if (!class->ascii_init_done)
    {
        class->ascii_init_done = true;
        /* Construction d'une chaîne adéquate */
        for (i = 0; i < 128; ++i)
            switch (i)
            {
                case 0 ... 31:
                    ascii_chars[i] = '?';
                    break;
                case 32 ... 127:
                    ascii_chars[i] = i;
                    break;
                default:
                    ascii_chars[i] = '?';
                    break;
            }
        /* Analyse de la chaîne créée */
        attribs = pango_attr_list_new();
        list = pango_itemize(context, ascii_chars, 0, 128, attribs, NULL);
        class->ascii_ready = (list != NULL && list->next == NULL);
        if (class->ascii_ready)
        {
            PangoItem   *item;
            int         width;
            item  = (PangoItem *)list->data;
            //width = gui.char_width * PANGO_SCALE;
            /* Remember the shape engine used for ASCII. */
            //default_shape_engine = item->analysis.shape_engine;
            class->ascii_font = item->analysis.font;
            g_object_ref(class->ascii_font);
            class->ascii_glyphs = pango_glyph_string_new();
            pango_shape(ascii_chars, 128, &item->analysis, class->ascii_glyphs);
            class->ascii_ready = (class->ascii_glyphs->num_glyphs == 128);
#if 0
            for (i = 0; i < class->ascii_glyphs->num_glyphs; i++)
            {
                PangoGlyphGeometry *geom;
                geom = &class->ascii_glyphs->glyphs[i].geometry;
                //geom->x_offset += MAX(0, width - geom->width) / 2;
                //geom->width = /*width*/8 * PANGO_SCALE;
            }
#endif
        }
        g_list_foreach(list, (GFunc)&pango_item_free, NULL);
        g_list_free(list);
        pango_attr_list_unref(attribs);
    }
    return class->ascii_ready;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte Pango pour l'analyse des caractères.      *
*                attribs = propriétés de la zone de texte.                    *
*                text    = chaîne de caractères à traiter.                    *
*                length  = quantité de ces caractères.                        *
*                                                                             *
*  Description : Crée un nouveau fragment de texte avec des propriétés.       *
*                                                                             *
*  Retour      : Composant GTK créé.                                          *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_buffer_segment_prepare(GBufferSegment *segment, PangoContext *context, PangoAttrList *attribs, const char *text, size_t length)
{
    PangoGlyphString *glyphs;               /* Caractères traités          */
    GList           *item_list;
    PangoItem   *item;
    PangoRectangle logical;
    char *max;
    char *iter;
    GBufferSegmentClass *class;
    size_t i;
    PangoGlyphInfo *info;
    gint *log_clusters;
    PangoGlyphInfo *ref;
    glyphs = pango_glyph_string_new();
    /**
     * Petite astuce empruntée à Vim...
     * (cf. src/gui_gtk_x11.c, fonction gui_gtk2_draw_string()).
     * On essaie de traiter à la main les morceaux de
     * texte. Pour ceux en ASCII pur, le gain est non négligeable.
     */
    max = text + length;
    for (iter = text; iter < max; iter++)
        if (*iter & 0x80)
            goto not_ascii;
    class = G_BUFFER_SEGMENT_GET_CLASS(segment);
    if (!ascii_glyph_table_init(class, context))
        goto not_ascii;
    pango_glyph_string_set_size(glyphs, length);
    info = glyphs->glyphs;
    log_clusters = glyphs->log_clusters;
    ref = class->ascii_glyphs->glyphs;
    for (i = 0; i < length; i++)
    {
        info[i] = ref[text[i]];
        log_clusters[i] = i;
    }
 
    goto next;
 not_ascii:
    item_list = pango_itemize(context, text, 0, length, attribs, NULL);
    /*
    if (!(item_list != NULL && item_list->next == NULL))
        printf("ouich\n");
    */
    item  = (PangoItem *)item_list->data;
    pango_shape(text, length, &item->analysis, glyphs);
    //segment->font = item->analysis.font;/* TODO : ref ! */
 next:
    //pango_shape(text, length, &item->analysis, glyphs);
    pango_glyph_string_extents(glyphs, class->ascii_font, NULL, &segment->logical);
    segment->logical.y /= PANGO_SCALE;
    segment->logical.width /= PANGO_SCALE;
    segment->logical.height /= PANGO_SCALE;
    segment->glyphs = glyphs;
}
/* Détermine le type du fragment de caractères aux propriétés communes. */
G_DEFINE_TYPE(GBufferSegment, g_buffer_segment, G_TYPE_OBJECT);
/******************************************************************************
*                                                                             *
*  Paramètres  : class = classe de composant GTK à initialiser.               *
*                                                                             *
*  Description : Procède à l'initialisation d'une classe de fragment de texte.*
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_buffer_segment_class_init(GBufferSegmentClass *class)
{
}
/******************************************************************************
*                                                                             *
*  Paramètres  : segment = composant GTK à initialiser.                       *
*                                                                             *
*  Description : Procède à l'initialisation d'un fragment de texte.           *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_buffer_segment_init(GBufferSegment *segment)
{
}
/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte Pango pour l'analyse des caractères.      *
*                attribs = propriétés de la zone de texte.                    *
*                text    = chaîne de caractères à traiter.                    *
*                length  = quantité de ces caractères.                        *
*                                                                             *
*  Description : Crée un nouveau fragment de texte avec des propriétés.       *
*                                                                             *
*  Retour      : Composant GTK créé.                                          *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GBufferSegment *g_buffer_segment_new(PangoContext *context, PangoAttrList *attribs, const char *text, size_t length)
{
    GBufferSegment *result;                 /* Composant à retourner       */
    result = g_object_new(G_TYPE_BUFFER_SEGMENT, NULL);
    //result = g_new(GBufferSegment, 1);
    g_buffer_segment_prepare(result, context, attribs, text, length);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : segment  = fragment de texte à consulter.                    *
*                                                                             *
*  Description : Fournit la quantité de pixels requise pour l'impression.     *
*                                                                             *
*  Retour      : Largeur requise par la colonne, en pixel.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
gint g_buffer_segment_get_width(const GBufferSegment *segment)
{
    return segment->logical.width;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : segment  = fragment de texte à manipuler.                    *
*                drawable = surface de rendu où travailler.                   *
*                gc       = contexte graphique à utiliser pour les pinceaux.  *
*                x        = abscisse du point d'impression (à maj). [OUT]     *
*                y        = ordonnée du point d'impression.                   *
*                                                                             *
*  Description : Imprime le fragment de texte représenté.                     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void g_buffer_segment_draw(GBufferSegment *segment, GdkDrawable *drawable, GdkGC *gc, gint *x, gint y)
{
    gdk_draw_glyphs(drawable, gc, G_BUFFER_SEGMENT_GET_CLASS(segment)->ascii_font,
                    *x, y - segment->logical.y, segment->glyphs);
    *x += segment->logical.width;
}