#include <gtk/gtk.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
//#include "gtksctext.h"

static int font_metrics[] = {0, 2, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 
					17, 18, 19, 20, 23, 24, 25, 26, 30, 33, 34, 36, 38,
					39, 40, 41, 42, 43, 44, 45, 46, 48, 49, 50, 60};

static int font_metric_size = sizeof(font_metrics)/sizeof(int);

static GData * font_cache;
static gboolean cache_init = FALSE;


/*
 * adjust font metric attempts to find a legal font size that best matches
 * the requested font
 */


int _adjust_font_metrics(int size)
{
	int i;
	for( i = 0; i < font_metric_size && font_metrics[i] < size; i++ );
	
	if(font_metrics[i] == size)
		return size;  // no correction was necessary :)

	/* just in case we didn't get a perfect match return whatever was closest */
	if(i > 0 && (size - font_metrics[i-1]) < (font_metrics[i] - size))
	{
		return font_metrics[i-1];
	}

	return font_metrics[i];
}


static GdkFont * _getfont(char * font, int bold, int italic, int size, int ptsize)
{
    gchar font_name[1024] = "-*-";
    GdkFont * my_font;

	if(!cache_init)
	{
		g_datalist_init(&font_cache);
		cache_init = TRUE;
	}

	
	size = _adjust_font_metrics(size);

    if(strlen(font))
    {
        strcat( font_name, font );
    }
    else
    {
    	strcat( font_name, "*" );
    }
    strcat( font_name, "-" );

    if(bold)
    {
        strcat( font_name, "bold");
    }
    else
    {
        strcat( font_name, "medium");
    }
    strcat( font_name, "-" );

    /*
     * here is the deal, some fonts have oblique but not italics
     * other fonts have italics but not oblique
     * so we are going to try both
     */

    if( italic == 1 )
    {
        strcat( font_name, "i");
    }
    else if( italic == 2 )
    {
        strcat( font_name, "o" );
    }
    else
    {
        strcat( font_name, "r");
    }
    strcat( font_name, "-*-*-");
    {
        char buff[256];
		if(size != -1)
		{
        	sprintf(buff, "%d-*-*-*-*-*-*-*", size);
		}
		else
		{
        	sprintf(buff, "*-%d-*-*-*-*-*-*", ptsize);
		}

        strcat( font_name, buff );
    }

    g_strdown(font_name);

    if(( my_font =
            g_datalist_id_get_data(&font_cache, g_quark_from_string(font_name)) ))
    {
        return my_font;
    }
    my_font = gdk_font_load(font_name);
    if( !my_font )
    {
        if( italic == 1 )
        {
            my_font = _getfont(font, bold, 2, size, ptsize );
        }
        else
        {
            my_font = _getfont("", bold, italic, size, ptsize );
        }
    }
    g_datalist_id_set_data( &font_cache,
                            g_quark_from_string(font_name),
                            my_font );
    return my_font;
}

static GdkColor * _getcolor(GdkColormap * map, char * name)
{
	GdkColor * color;

	color = (GdkColor *)g_new0(GdkColor, 1);
	gdk_color_parse(name, color);
	gdk_color_alloc(map, color);
	
	return color;
}

static void _extract_parameter(char * parm, char * buffer, int buffer_size)
{
	/*
	 *if we start with a quote, we should end with a quote
	 * other wise we end with a space
	 */

	if(*parm == '\"')
	{
		int cnt = 0;
		parm += 1;

		for(cnt = 0; *parm && *parm != '\"' && cnt < buffer_size-1; 
				parm++, cnt++)
		{
			buffer[cnt] = *parm;
		}
		buffer[cnt] = '\0';
	}
	else
	{
		int cnt = 0;

		for(cnt = 0; *parm && !isspace(*parm) && cnt < buffer_size-1; 
				parm++, cnt++)
		{
			buffer[cnt] = *parm;
		}
		buffer[cnt] = '\0';
	}
}

typedef struct _font_stack
{
	GdkColor * fore;
	GdkColor * back;
	char font_name[255];
	int font_size;
	int font_ptsize;
	struct _font_stack * next;
} font_stack;

static font_stack * _font_stack_init()
{
	font_stack * fs = g_new0(font_stack, 1);

	fs->font_size = 12;
	strcpy(fs->font_name, "helvetica");

	return fs;
}

static font_stack * _font_stack_push(font_stack * fs)
{
	font_stack * fs2 = g_new0(font_stack, 1);
	memcpy(fs2, fs, sizeof(font_stack));
	fs2->next = fs;

	if(fs2->fore)
	{
		fs2->fore = g_new0(GdkColor, 1);
		memcpy(fs2->fore, fs->fore, sizeof(GdkColor));
	}

	if(fs2->back)
	{
		fs2->back = g_new0(GdkColor, 1);
		memcpy(fs2->back, fs->back, sizeof(GdkColor));
	}

	return fs2;
}

static font_stack * _font_stack_pop(font_stack * fs)
{

	font_stack * fs2 = fs->next;
	if(fs->fore)
		g_free(fs->fore);
	if(fs->back)
		g_free(fs->back);
	g_free(fs);

	return fs2;
}


void gtk_eb_html_add(GtkText* widget, char * text)
{
	gchar ** tokens = g_strsplit(text, "<", 0);
	int i = 0;
	GdkFont * font = NULL;
	int font_bold = 0;
	int font_italic = 0;
	int first = 0;

	font_stack * fs = _font_stack_init();

	

	/*
	 * Since the first thing in a split list may or may not be starting with a
	 * < we need to keep track of that manually
	 */
	
	if(*text != '<')
	{
		first = 1;
	}

	for( i = 0; tokens[i]; i++ )
	{

		if(strlen(tokens[i]) == 0)
			continue;

		fprintf(stderr, "Part %d: %s\n", i, tokens[i]);

		if(!first && strstr(tokens[i], ">"))
		{
			gchar ** tags = g_strsplit(tokens[i], ">", 2);
			/*
			 * Okay, we now know that tags[0] is an html tag because
			 * there was a < before it and a > after it
			 */

			/*
			 * first let's clean it up
			 */

			fprintf(stderr, "\t Part a: %s\n", tags[0]);
			fprintf(stderr, "\t Part b: %s\n", tags[1]);
			g_strstrip(tags[0]);
			g_strdown(tags[0]);
	
			if(!strncmp(tags[0], "font", strlen("font")))
			{
				char * data = tags[0] + strlen("font");
				char * parm;

				fs = _font_stack_push(fs);

				if((parm = strstr(data, "face=")) != NULL)
				{
					parm += strlen("face=");
					_extract_parameter(parm, fs->font_name, 255);
				}

				if((parm = strstr(data, "ptsize=")) != NULL)
				{
					char size[25];
					parm += strlen("ptsize=");
					_extract_parameter(parm, size, 25);
					fs->font_size = -1;
					fs->font_ptsize = atoi(size);
				}
				else if((parm = strstr(data, "size=")) != NULL)
				{
					char size[25];
					parm += strlen("size=");
					_extract_parameter(parm, size, 25);

					if(*size == '+')
					{
						fs->font_size += atoi(size+1);
					}
					else if(*size == '-')
					{
						fs->font_size -= atoi(size+1);
					}
					else
					{
						fs->font_size = atoi(size);
					}
				}
				if((parm = strstr(data, "color=")) != NULL)
				{
					char color[255];
					parm += strlen("color=");
					_extract_parameter(parm, color, 255);
					if(fs->fore)
					{
						g_free(fs->fore);
					}
					fs->fore = _getcolor(gdk_window_get_colormap(GTK_WIDGET(widget)->window), color);
						
				}

				font = _getfont(fs->font_name, font_bold, font_italic, 
						fs->font_size, fs->font_ptsize);
			}
			else if(!strcmp(tags[0], "b"))
			{
				font_bold = 1;
				font = _getfont(fs->font_name, font_bold, font_italic, 
						fs->font_size, fs->font_ptsize);
			}
			else if(!strcmp(tags[0], "/b"))
			{
				font_bold = 0;
				font = _getfont(fs->font_name, font_bold, font_italic, 
						fs->font_size, fs->font_ptsize);
			}
			else if(!strcmp(tags[0], "i"))
			{
				font_italic = 1;
				font = _getfont(fs->font_name, font_bold, font_italic, 
						fs->font_size, fs->font_ptsize);
			}
			else if(!strcmp(tags[0], "/i"))
			{
				font_italic = 0;
				font = _getfont(fs->font_name, font_bold, font_italic, 
						fs->font_size, fs->font_ptsize);
			}
			else if(!strcmp(tags[0], "br"))
			{
				gtk_text_insert(widget, font, fs->fore, fs->back, 
						"\n", strlen("\n"));
			}
			else if(!strcmp(tags[0], "/font")
					|| !strcmp(tags[0], "/body"))
			{
				fs = _font_stack_pop(fs);
				
			
	/*
	 * we don't want to pop off the very first element in the stack
	 * because that is the defaults, if we are in a positon of trying
	 * that means the user tried doing one too many </font>'s
	 * heh
	 */
				
				if(!fs)
				{
					fs = _font_stack_init();
				}
				font = _getfont(fs->font_name, font_bold, font_italic, 
						fs->font_size, fs->font_ptsize);
			}
			else if(!strncmp(tags[0], "body", strlen("body")))
			{
				char * data = tags[0] + strlen("body");
				char * parm;


				fs = _font_stack_push(fs);

				if((parm = strstr(data, "bgcolor=")) != NULL)
				{
					char color[255];
					parm += strlen("bgcolor=");
					_extract_parameter(parm, color, 255);
					if(fs->back)
					{
						g_free(fs->back);
					}
					fs->back = _getcolor(gdk_window_get_colormap(GTK_WIDGET(widget)->window), color);
						
				}
			}

			if(tags[1])
			{
				gtk_text_insert(widget, font, fs->fore, fs->back,
								tags[1], strlen(tags[1]));
			}
			g_strfreev(tags);
		}
		else
		{
			/*
			 * Otherwise then it is all just text
			 */

			/*
			 * first we were not supposed to have gotten rid of that < ;P
			 */

			if(!first)
			{
				gtk_text_insert( widget, font, fs->fore, fs->back,
								"<", strlen("<") );
			}

			gtk_text_insert(widget, font, fs->fore, fs->back,
								tokens[i], strlen(tokens[i]));

			first = 0;
		}
	}
	g_strfreev(tokens);

	while(fs)
	{
		fs = _font_stack_pop(fs);
	}
}
