#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#include <cairo.h>
#include <pango/pango.h>
#include <pango/pangocairo.h>
#include <glib/gprintf.h>


#define FONT    "Monospace Bold 12"

#define TEXT_TYPE   1

#if TEXT_TYPE == 0
#define TEXT    "#savefile-version 6" \
                "#host 208.185.247.29 23" \
                "#delim normal" \
                "#groupdelim @" \
                "#file =" \
                "#exe <powwow/load.conf" \
                "#option -exit -history -words +compact -debug. -echo -info " \
                    "-key.echo -speed.walk" \
                "#option -wrap -autoprint -reprint -sendsize. +auto.clear"
#elif TEXT_TYPE == 1
#define TEXT    "aaaaaaaa.bbbbbbbb cccccccc,dddddddd-eeeeeeee;ffffffff:" \
                "gggggggg\"hhhhhhhh'iiiiiiii/jjjjjjjj\\kkkkkkkk(llllllll)" \
                "mmmmmmmm"
#endif
#define CHARACTER   "A"

/* Prototypes *****************************************************************/

gboolean        expose_event            (GtkWidget* const       widget,
                                         GdkEventExpose* const  event,
                                         gpointer               data);

void            draw_grid               (cairo_t* const         cr,
                                         gint                   width,
                                         gint                   height);

PangoAttrList * get_text_attributes     (gint                   length);

gboolean        delete_event            (GtkWidget* const       widget,
                                         GdkEvent* const        event,
                                         gpointer               data);

void            destroy                 (GtkWidget* const       widget,
                                         gpointer               data);


/* Variables ******************************************************************/


/* Functions ******************************************************************/

gboolean
expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
    cairo_t                     *cr;
    PangoFontDescription        *font;
    PangoLayout                 *layout;
    PangoAttrList               *attributes;
    gint                         width, height, wrap;
    
    /* create cairo context */
    cr = gdk_cairo_create (widget-> window);
    
    /* clear cairo surface */
    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
    cairo_rectangle (cr,
                     0.0,
                     0.0,
                     (gdouble)(event-> area.width),
                     (gdouble)(event-> area.height));
    cairo_fill (cr);
    
    /* create pango layout */
    layout = pango_cairo_create_layout (cr);
    
    /* set up font description */
    font = pango_font_description_from_string (FONT);
    pango_layout_set_font_description (layout, font);
    
    /* get dimensions of CHARACTER */
    pango_layout_set_text (layout, CHARACTER, 1);
    pango_layout_get_pixel_size (layout, &width, &height);
    
    /* draw background grid */
    draw_grid (cr, width, height);
    
    /* set pango text to TEXT */
    wrap = ((widget-> allocation.width) / width) * width;
    pango_layout_set_width (layout, wrap * PANGO_SCALE);
    pango_layout_set_wrap (layout, PANGO_WRAP_CHAR);
    pango_layout_set_text (layout, TEXT, -1);
    
    /* set pango text attributes */
    attributes = get_text_attributes ((gint)strlen (TEXT));
    pango_layout_set_attributes (layout, attributes);
    
    /* display the layout */
    pango_cairo_show_layout (cr, layout);
    
    /* clean up */
    cairo_destroy (cr);
    g_object_unref (layout);
    pango_font_description_free (font);
    
    return FALSE;
}

/* draws a grid of rectangles */
void
draw_grid (cairo_t* const cr, gint width, gint height)
{
    gdouble                 left, right, top, bottom;
    gint                    rows, cols, r, c, i;
    
    cairo_push_group (cr);
    
    /* get clip size */
    cairo_clip_extents (cr, &left, &top, &right, &bottom);
    
    /* calculate how many rows and columns to draw */
    cols = (gint)(right - left) / width;
    rows = (gint)(bottom - top) / height;
    
    /* draw grids */
    for (r = 0; r < (rows+1); r++)
    {
        i = r;
        for (c = 0; c < (cols+1); c++)
        {
            /* if i is odd */
            if (i & 1)
                cairo_set_source_rgb (cr, 0.6, 0.6, 0.6);
            else
                cairo_set_source_rgb (cr, 0.4, 0.4, 0.4);
            
            cairo_rectangle (cr,
                             left + (gdouble)(c * width),
                             top + (gdouble)(r * height),
                             (gdouble)width,
                             (gdouble)height);
            
            cairo_fill (cr);
            i++;
        }
    }
    
    cairo_pop_group_to_source (cr);
    cairo_paint (cr);
}

/* helpers for get_text_attributes () */
typedef struct
{
    guint16 red;
    guint16 green;
    guint16 blue;
} color16;

#define TO_16(dbl)    ((guint16)(dbl * 65535.0))

PangoAttrList *
get_text_attributes (gint length)
{
    PangoAttrList       *list;
    PangoAttribute      *attribute;
    gint                 a, b;
    color16              colors[] = { {TO_16 (1.0), TO_16 (0.0), TO_16 (0.0)},     /* red */
                                      {TO_16 (1.0), TO_16 (0.6), TO_16 (0.0)},     /* orange */
                                      {TO_16 (1.0), TO_16 (1.0), TO_16 (0.0)},     /* yellow */
                                      {TO_16 (0.0), TO_16 (1.0), TO_16 (0.0)},     /* green */
                                      {TO_16 (0.0), TO_16 (0.0), TO_16 (1.0)},     /* blue */
                                      {TO_16 (1.0), TO_16 (0.0), TO_16 (1.0)}, };  /* purple */
    
    /* initialize the list */
    list = pango_attr_list_new ();
    
    /* for each letter, a, and each color, b */
    for (a = 0, b = 0; a < length; a++, b++)
    {
        if (b >= 6)
            b = 0;
        /* create a new attribute */
        attribute = pango_attr_foreground_new (colors[b].red, colors[b].green, colors[b].blue);
        attribute-> start_index = a;
        attribute-> end_index = a + 1;
        
        /* add attribute to list */
        pango_attr_list_insert (list, attribute);
    }
    
    return list;
}

/* close window was clicked, return FALSE to destroy the window */
gboolean
delete_event (GtkWidget* const widget, GdkEvent* const event, gpointer data)
{
    return FALSE;
}

/* stop the program */
void
destroy (GtkWidget* const widget, gpointer data)
{
    gtk_main_quit ();
}

/* initialize the gui */
void
gui_init (void)
{
    GtkWidget       *window;
    GtkWidget       *hbox;
    GtkWidget       *drawingArea;
    
    /* create a new window */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    
    /* connect callback for delete event */
    g_signal_connect (G_OBJECT (window),
                      "delete-event",
                      G_CALLBACK (delete_event),
                      NULL);
    
    /* connect callback for destroy event */
    g_signal_connect (G_OBJECT (window),
                      "destroy",
                      G_CALLBACK (destroy),
                      NULL);

    /* set window border width */
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);
    
    /* create new hbox */
    hbox = gtk_hbox_new (TRUE, 10);
    
    /* create a new drawing area */
    drawingArea = gtk_drawing_area_new ();
    gtk_widget_set_size_request (drawingArea, 50, 50);
    
    /* connect callback for expose event */
    g_signal_connect (drawingArea,
                      "expose-event",
                      G_CALLBACK (expose_event),
                      NULL);

    /* pack widget together */
    gtk_box_pack_start (GTK_BOX (hbox), drawingArea, TRUE, TRUE, 0);
    gtk_container_add (GTK_CONTAINER (window), hbox);
    
    /* show the widgets */
    gtk_widget_show (drawingArea);
    gtk_widget_show (hbox);
    gtk_widget_show (window);
}

gint
main (gint argc, gchar **argv)
{
    /* initialize gtk */
    gtk_init (&argc, &argv);
    
    /* create gui */
    gui_init ();
    
    /* begin event loop */
    gtk_main ();
    
    exit (EXIT_SUCCESS);
}
