/* Chrysalide - Outil d'analyse de fichiers binaires
 * gtklinkrenderer.c - liens graphiques entre différents morceaux de code
 *
 * Copyright (C) 2009-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 "gtklinkrenderer.h"


#include <math.h>



/* Lien entre morceaux de code (instance) */
struct _GtkLinkRenderer
{
    GtkWidget/*Object*/ parent;                       /* A laisser en premier        */

    LinkColor color;                        /* Couleur d'impression        */
    GdkPoint *points;                       /* Points de la ligne dessinée */
    size_t count;                           /* Quantité de ces points      */

};


/* Lien entre morceaux de code (classe) */
struct _GtkLinkRendererClass
{
    GtkWidgetClass/*ObjectClass*/ parent;                  /* A laisser en premier        */

};


#define ARROW_LENGHT 10
#define ARROW_DEGREES 10


/* Initialise la classe générique des liens graphiques. */
static void gtk_link_renderer_class_init(GtkLinkRendererClass *);

/* Initialise une instance de lien graphique entre codes. */
static void gtk_link_renderer_init(GtkLinkRenderer *);

/* Dessine une flèche au bout du lien représenté. */
static void draw_link_arrow(cairo_t *, gint, gint, gint, gint);



/* Détermine le type du moteur de rendu pour les liens graphiques. */
G_DEFINE_TYPE(GtkLinkRenderer, gtk_link_renderer, GTK_TYPE_WIDGET/*OBJECT*/)


/******************************************************************************
*                                                                             *
*  Paramètres  : class = classe GTK à initialiser.                            *
*                                                                             *
*  Description : Initialise la classe générique des liens graphiques.         *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void gtk_link_renderer_class_init(GtkLinkRendererClass *class)
{

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view = instance GTK à initialiser.                           *
*                                                                             *
*  Description : Initialise une instance de lien graphique entre codes.       *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void gtk_link_renderer_init(GtkLinkRenderer *view)
{

}


/******************************************************************************
*                                                                             *
*  Paramètres  : color  = couleur d'impression.                               *
ù                points = points consituant la ligne à représenter.           *
*                count  = nombre de ces points.                               *
*                                                                             *
*  Description : Crée un nouveau moteur de rendu pour les liens graphiques.   *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void/*GtkObject*/ *gtk_link_renderer_new(LinkColor color, GdkPoint *points, size_t count)
{
    GtkLinkRenderer *result;                /* Moteur de rendu à retourner */

    result = g_object_new(GTK_TYPE_LINK_RENDERER, NULL);

    result->color = color;
    result->points = points;
    result->count = count;

    return /*GTK_OBJECT*/(result);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : renderer    = moteur de rendu à manipuler.                   *
*                requisition = dimensions adaptées validées. [OUT]            *
*                                                                             *
*  Description : S'assure qu'une zone sera assez grande pour tout contenir.   *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void gtk_link_renderer_size_request(const GtkLinkRenderer *renderer, GtkRequisition *requisition)
{
    size_t i;                               /* Boucle de parcours          */

    for (i = 0; i < renderer->count; i++)
    {
        requisition->width = MAX(requisition->width, renderer->points[i].x);
        requisition->height = MAX(requisition->height, renderer->points[i].y);
    }

}

#if 0
/******************************************************************************
*                                                                             *
*  Paramètres  : renderer = moteur de rendu à manipuler.                      *
*                drawable = surface de rendu à utiliser.                      *
*                gc       = contexte graphique du dessin.                     *
*                                                                             *
*  Description : Dessine les liens graphiques enregistrés dans le moteur.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void gtk_link_renderer_draw(const GtkLinkRenderer *renderer, GdkDrawable *drawable, GdkGC *gc)
{
    cairo_t *cairo;                         /* Gestionnaire de rendu       */

    cairo = gdk_cairo_create(drawable);

    _gtk_link_renderer_draw(renderer, cairo, true);

    cairo_destroy(cairo);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : renderer = moteur de rendu à manipuler.                      *
*                cairo    = assistant pour le rendu graphique.                *
*                arrow    = indique le besoin en flèche à l'arrivée.          *
*                                                                             *
*  Description : Dessine les liens graphiques enregistrés dans le moteur.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void _gtk_link_renderer_draw(const GtkLinkRenderer *renderer, cairo_t *cairo, bool arrow)
{
    size_t i;                               /* Boucle de parcours          */

    switch (renderer->color)
    {
        default:
        case LKC_DEFAULT:
            cairo_set_source_rgb(cairo, 0, 0, 0);
            break;
        case LKC_GREEN:
            cairo_set_source_rgb(cairo, 0, 0.6, 0);
            break;
        case LKC_RED:
            cairo_set_source_rgb(cairo, 0.8, 0, 0);
            break;
        case LKC_BLUE:
            cairo_set_source_rgb(cairo, 0, 0, 0.8);
            break;
        case LKC_DASHED_GRAY:
            cairo_set_source_rgb(cairo, 0.4, 0.4, 0.4);
            break;
    }

    switch (renderer->color)
    {
        default:
        case LKC_DEFAULT:
        case LKC_GREEN:
        case LKC_RED:
        case LKC_BLUE:
            cairo_set_dash(cairo, (double []) { 6.0 }, 0, 0.0);
            break;
        case LKC_DASHED_GRAY:
            cairo_set_dash(cairo, (double []) { 6.0 }, 1, 0.0);
            break;
    }

    /**
     * Si on ne veut pas de flèche, on doit se destiner à un aperçu...
     * Dans ce cas, pour plus de lisibilité, on double la taille d'impression.
     * Et pour faire ressortir les boucles, on double encore les liens associés.
     */
    cairo_set_line_width(cairo, arrow ? 1 : (renderer->color == LKC_BLUE ? 4 : 2));

    cairo_move_to(cairo, renderer->points[0].x + 0.5, renderer->points[0].y);

    for (i = 1; i < renderer->count; i++)
        cairo_line_to(cairo, renderer->points[i].x + 0.5, renderer->points[i].y);

    cairo_stroke(cairo);

    if (arrow)
        draw_link_arrow(cairo,
                        renderer->points[renderer->count - 2].x,
                        renderer->points[renderer->count - 2].y,
                        renderer->points[renderer->count - 1].x,
                        renderer->points[renderer->count - 1].y);

}
#endif

/******************************************************************************
*                                                                             *
*  Paramètres  : cairo   = gestionnaire de rendu graphique.                   *
*                start_x = abscisse du début du segment final.                *
*                start_y = ordonnée du début du segment final.                *
*                end_x   = abscisse de fin de segment et pointe de flèche.    *
*                end_y   = ordonnée de fin de segment et pointe de flèche.    *
*                                                                             *
*  Description : Dessine une flèche au bout du lien représenté.               *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void draw_link_arrow(cairo_t *cairo, gint start_x, gint start_y, gint end_x, gint end_y)
{
    double angle;                           /* Angle du triangle à remplir */
    double factor_x;                        /* Direction de la flèche #1   */
    double factor_y;                        /* Direction de la flèche #2   */
    double x1;                              /* Abscisse du point n°1       */
    double y1;                              /* Ordonnée du point n°1       */
    double x2;                              /* Abscisse du point n°2       */
    double y2;                              /* Ordonnée du point n°2       */

    angle = atan2(end_y - start_y, end_x - start_x) + M_PI;

    factor_x = -1;
    factor_y = -1;

    x1 = end_x + factor_x * ARROW_LENGHT * cos(angle - ARROW_DEGREES);
    y1 = end_y + factor_y * ARROW_LENGHT * sin(angle - ARROW_DEGREES);
    x2 = end_x + factor_x * ARROW_LENGHT * cos(angle + ARROW_DEGREES);
    y2 = end_y + factor_y * ARROW_LENGHT * sin(angle + ARROW_DEGREES);

    cairo_move_to(cairo, end_x, end_y);

    cairo_line_to(cairo, x1, y1);
    cairo_line_to(cairo, x2, y2);

    cairo_move_to(cairo, end_x, end_y);

    cairo_fill(cairo);

}