/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * Copyright (C) 1999 Free Software Foundation
 * Copyright (C) 2000, 2001 Eazel, Inc.
 * Copyright (C) 2001 Mikael Hallendal <micke@imendio.com>
 * Copyright (C) 2004 Imendio AB
 *
 * This program 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 2 of the
 * License, or (at your option) any later version.
 *
 * This program 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 this program; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include <config.h>
#include <string.h>
#include <stdlib.h>
#include <gtk/gtklabel.h>
#include "dh-util.h"

#define d(x)

static gchar *dot_dir = NULL;

static void        tagify_bold_labels (GladeXML    *xml);
static GladeXML *  get_glade_file     (const gchar *filename,
				       const gchar *root,
				       const gchar *domain,
				       const gchar *first_required_widget,
				       va_list      args);

static void
tagify_bold_labels (GladeXML *xml)
{
        const gchar *str;
        gchar       *s;
        GtkWidget   *label;
        GList       *labels, *l;
 
        labels = glade_xml_get_widget_prefix (xml, "boldlabel");
 
        for (l = labels; l; l = l->next) {
                label = l->data;
 
                if (!GTK_IS_LABEL (label)) {
                        g_warning ("Not a label, check your glade file.");
                        continue;
                }
  
                str = gtk_label_get_text (GTK_LABEL (label));
 
                s = g_strdup_printf ("<b>%s</b>", str);
                gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
                gtk_label_set_label (GTK_LABEL (label), s);
                g_free (s);
        }
 
        g_list_free (labels);
}

static GladeXML *
get_glade_file (const gchar *filename,
                const gchar *root,
                const gchar *domain,
                const gchar *first_required_widget,
		va_list args)
{
        GladeXML   *gui;
        const char *name;
        GtkWidget **widget_ptr;
 
        gui = glade_xml_new (filename, root, domain);
        if (!gui) {
                g_warning ("Couldn't find necessary glade file '%s'", filename);                return NULL;
        }
 
        for (name = first_required_widget; name; name = va_arg (args, char *)) {                widget_ptr = va_arg (args, void *);
                 
                *widget_ptr = glade_xml_get_widget (gui, name);
                 
                if (!*widget_ptr) {
                        g_warning ("Glade file '%s' is missing widget '%s'.",
                                   filename, name);
                        continue;
                }
        }
 
        tagify_bold_labels (gui);
         
        return gui;
}

const gchar *
dh_dot_dir (void)
{
	if (!dot_dir) {
		dot_dir = g_build_filename (g_get_home_dir (),
					    ".gnome2",
					    "devhelp",
					    NULL);
	}

	return dot_dir;
}


GladeXML *
dh_glade_get_file (const gchar *filename,
		   const gchar *root,
		   const gchar *domain,
		   const gchar *first_required_widget,
		   ...)
{
        va_list   args;
        GladeXML *gui;
 
        va_start (args, first_required_widget);
 
        gui = get_glade_file (filename,
                              root,
                              domain,
                              first_required_widget,
                              args);
                                                                                
        va_end (args);
                                                                                
        if (!gui) {
                return NULL;
        }
                                                                                
        return gui;
}

void
dh_glade_connect (GladeXML *gui,
		  gpointer  user_data,
		  gchar    *first_widget,
		  ...)
{
        va_list      args;
        const gchar *name;
        const gchar *signal;
        GtkWidget   *widget;
        gpointer    *callback;
                                                                                
        va_start (args, first_widget);
                                                                                
        for (name = first_widget; name; name = va_arg (args, char *)) {
                signal = va_arg (args, void *);
                callback = va_arg (args, void *);
                                                                                
                widget = glade_xml_get_widget (gui, name);
                if (!widget) {
                        g_warning ("Glade file is missing widget '%s', aborting",
                                   name);
                        continue;
                }
                                                                                
                g_signal_connect (widget,
                                  signal,
                                  G_CALLBACK (callback),
                                  user_data);
        }
                                                                                
        va_end (args);
}



/* ----------------------------------------------------------------- */
/*                          From GNOME VFS                           */
/* ----------------------------------------------------------------- */
static void
remove_internal_relative_components (char *uri_current)
{
	char *segment_prev, *segment_cur;
	size_t len_prev, len_cur;

	len_prev = len_cur = 0;
	segment_prev = NULL;

	segment_cur = uri_current;

	while (*segment_cur) {
		len_cur = strcspn (segment_cur, "/");

		if (len_cur == 1 && segment_cur[0] == '.') {
			/* Remove "." 's */
			if (segment_cur[1] == '\0') {
				segment_cur[0] = '\0';
				break;
			} else {
				memmove (segment_cur, segment_cur + 2, strlen (segment_cur + 2) + 1);
				continue;
			}
		} else if (len_cur == 2 && segment_cur[0] == '.' && segment_cur[1] == '.' ) {
			/* Remove ".."'s (and the component to the left of it) that aren't at the
			 * beginning or to the right of other ..'s
			 */
			if (segment_prev) {
				if (! (len_prev == 2
				       && segment_prev[0] == '.'
				       && segment_prev[1] == '.')) {
				       	if (segment_cur[2] == '\0') {
						segment_prev[0] = '\0';
						break;
				       	} else {
						memmove (segment_prev, segment_cur + 3, strlen (segment_cur + 3) + 1);

						segment_cur = segment_prev;
						len_cur = len_prev;

						/* now we find the previous segment_prev */
						if (segment_prev == uri_current) {
							segment_prev = NULL;
						} else if (segment_prev - uri_current >= 2) {
							segment_prev -= 2;
							for ( ; segment_prev > uri_current && segment_prev[0] != '/' 
							      ; segment_prev-- );
							if (segment_prev[0] == '/') {
								segment_prev++;
							}
						}
						continue;
					}
				}
			}
		}

		/*Forward to next segment */

		if (segment_cur [len_cur] == '\0') {
			break;
		}
		 
		segment_prev = segment_cur;
		len_prev = len_cur;
		segment_cur += len_cur + 1;	
	}
	
}

gboolean
dh_util_uri_is_relative (const char *uri)
{
	const char *current;

	/* RFC 2396 section 3.1 */
	for (current = uri ; 
		*current
		&& 	((*current >= 'a' && *current <= 'z')
			 || (*current >= 'A' && *current <= 'Z')
			 || (*current >= '0' && *current <= '9')
			 || ('-' == *current)
			 || ('+' == *current)
			 || ('.' == *current)) ;
	     current++);

	return  !(':' == *current);
}

gchar *
dh_util_uri_relative_new (const gchar *uri, const gchar *base_uri)
{
	char *result = NULL;

	g_return_val_if_fail (base_uri != NULL, g_strdup (uri));
	g_return_val_if_fail (uri != NULL, NULL);

	/* See section 5.2 in RFC 2396 */

	/* FIXME bugzilla.eazel.com 4413: This function does not take
	 * into account a BASE tag in an HTML document, so its
	 * functionality differs from what Mozilla itself would do.
	 */

	if (dh_util_uri_is_relative (uri)) {
		char *mutable_base_uri;
		char *mutable_uri;

		char *uri_current;
		size_t base_uri_length;
		char *separator;

		/* We may need one extra character
		 * to append a "/" to uri's that have no "/"
		 * (such as help:)
		 */

		mutable_base_uri = g_malloc(strlen(base_uri)+2);
		strcpy (mutable_base_uri, base_uri);
		
		uri_current = mutable_uri = g_strdup (uri);

		/* Chew off Fragment and Query from the base_url */

		separator = strrchr (mutable_base_uri, '#'); 

		if (separator) {
			*separator = '\0';
		}

		separator = strrchr (mutable_base_uri, '?');

		if (separator) {
			*separator = '\0';
		}

		if ('/' == uri_current[0] && '/' == uri_current [1]) {
			/* Relative URI's beginning with the authority
			 * component inherit only the scheme from their parents
			 */

			separator = strchr (mutable_base_uri, ':');

			if (separator) {
				separator[1] = '\0';
			}			  
		} else if ('/' == uri_current[0]) {
			/* Relative URI's beginning with '/' absolute-path based
			 * at the root of the base uri
			 */

			separator = strchr (mutable_base_uri, ':');

			/* g_assert (separator), really */
			if (separator) {
				/* If we start with //, skip past the authority section */
				if ('/' == separator[1] && '/' == separator[2]) {
					separator = strchr (separator + 3, '/');
					if (separator) {
						separator[0] = '\0';
					}
				} else {
				/* If there's no //, just assume the scheme is the root */
					separator[1] = '\0';
				}
			}
		} else if ('#' != uri_current[0]) {
			/* Handle the ".." convention for relative uri's */

			/* If there's a trailing '/' on base_url, treat base_url
			 * as a directory path.
			 * Otherwise, treat it as a file path, and chop off the filename
			 */

			base_uri_length = strlen (mutable_base_uri);
			if ('/' == mutable_base_uri[base_uri_length-1]) {
				/* Trim off '/' for the operation below */
				mutable_base_uri[base_uri_length-1] = 0;
			} else {
				separator = strrchr (mutable_base_uri, '/');
				if (separator) {
					*separator = '\0';
				}
			}

			remove_internal_relative_components (uri_current);

			/* handle the "../"'s at the beginning of the relative URI */
			while (0 == strncmp ("../", uri_current, 3)) {
				uri_current += 3;
				separator = strrchr (mutable_base_uri, '/');
				if (separator) {
					*separator = '\0';
				} else {
					/* <shrug> */
					break;
				}
			}

			/* handle a ".." at the end */
			if (uri_current[0] == '.' && uri_current[1] == '.' 
			    && uri_current[2] == '\0') {

			    	uri_current += 2;
				separator = strrchr (mutable_base_uri, '/');
				if (separator) {
					*separator = '\0';
				}
			}

			/* Re-append the '/' */
			mutable_base_uri [strlen(mutable_base_uri)+1] = '\0';
			mutable_base_uri [strlen(mutable_base_uri)] = '/';
		}

		result = g_strconcat (mutable_base_uri, uri_current, NULL);
		g_free (mutable_base_uri); 
		g_free (mutable_uri); 

	} else {
		result = g_strdup (uri);
	}
	
	return result;
}




syntax highlighted by Code2HTML, v. 0.9.1