/* Bluefish HTML Editor
 * attrpage.c - page of properties of tags
 *
 * Copyright (C) 2001 Santiago Capel Torres
 *
 * 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 <gtk/gtk.h>
#include <string.h>				/* strstr */
#include <ctype.h>				/* isalnum */
#include "default_include.h"
#include "bluefish.h"
#ifdef ATTRPAGE
#include "gtk_easy.h"
#include "parsedtd.h"			/* dtd stuff */
#include "attrpage.h"			/* myself */
#include "document.h"			/* doc_attr_page_refresh() */
#include "coloursel.h"			/* color_but_new () */
#include "html2.h"				/* style_but_new */

void insert_text(const gchar * newstring);	/* No esta en document.h */

typedef struct {
	gint start;
	gint end;
} Ttagposition;

static Ttagposition current_tag_position = { 0, -1 };
static int modifingdocument = 0;

static int nusedwidgets;
static int usedattrs[40];
static GtkWidget *usedwidgets[40];

static gchar *get_text_of_tag(gint pos, Ttagposition * position)
{
	GtkEditable *editable;
	gchar *text = NULL;
	gint ipos, len;

	editable = GTK_EDITABLE(main_v->current_document->textbox);
	text = gtk_editable_get_chars(editable, 0, -1);
	ipos = pos;
	len = strlen(text);

	/* go back until start of text, or we hit a '<' or '>' */
	while ((--pos) >= 0) {
		if (text[pos] == '<') {
			break;
		} else if (text[pos] == '>') {
			return NULL;
		}
	}
	if (pos < 0)
		return NULL;

	position->start = pos;

	/* we have an opening, but do we have a closing? */
	while (ipos < len) {
		if (text[ipos] == '<') {
			return NULL;
		} else if (text[ipos] == '>') {
			position->end = ipos + 1;
			return g_strndup(text + position->start + 1, ipos - pos - 1);
		}
		ipos++;
	}
	return NULL;
}

static gchar *get_value_from_text(gchar * attname, gchar * text)
{
	int inquotes = 0;
	int anequal = 0;
	int namelen = strlen(attname);
	gchar *ptext = text, *pvalue, *value;
	gchar endchar;

	while (*ptext) {
		if (*ptext == '"')
			inquotes = !inquotes;
		else if (!inquotes) {
			if (*ptext == *attname) {
				if (g_strncasecmp(ptext, attname, namelen) == 0
					&& (*(ptext - 1) == '"' || *(ptext - 1) == ' ')) {
					ptext += namelen;
					if (*ptext && !isalnum(*ptext)) {
						while (*ptext && *ptext == ' ')
							ptext++;
						if (*ptext == '=') {
							anequal = 1;
							ptext++;
						}
						while (*ptext && *ptext == ' ')
							ptext++;
						if (*ptext == '"' || *ptext == '\'')
							endchar = *ptext;
						else
							endchar = ' ';
						pvalue = ++ptext;
						while (*pvalue && *pvalue != endchar)
							pvalue++;
						value = g_strndup(ptext, pvalue - ptext);
						if (!anequal && endchar == ' ') {
							gchar attlist[1024] = ",";
							getDTDAttList(HTML_doctypes
										  [main_v->current_document->
										   dtd_loaded].elements,
										  attlist + 1, ",", 1024 - 1);
							g_strup(attlist);
							if (strstr(attlist, value) == NULL)
								return value;
							else
								return NULL;
						} else {
							return value;
						}
					}
				}
			}
		}
		ptext++;
	}
	return NULL;
}

/*  Function: set_value_in_text
 *  Description:
 *    Sets the text of a given attribute in the text of a given tag
 *    If the attribute doesn't exist, adds both the name and the value
 *    If the attribute exists but the value is the same, dont do nothing
 *    If the attribute exists but the value is diferent, replace the value
 *    If the attribute exists but the value is "", delete the whole attribute
 *  Arguments:
 *    attname - Name of the attribute to insert
 *    newvalue - Value of the attribute to insert
 *    text - Text of the tag where insert (or delete or replace) in
 *    startpos - Start in the whole text of that tag
 */
static void set_value_in_text(gchar * attname, gchar * newvalue,
							  gchar * text, gint startpos)
{
	int inquotes = 0;
	int anequal = 0;
	int namelen = strlen(attname);
	int pos;
	gchar *ptext = text, *pvalue = NULL, *value, *attrstart;
	gchar endchar;

	DEBUG_MSG("set_value_in_text:%s, %s, %s, %d\n", attname, newvalue,
			  text, startpos);

	modifingdocument = 1;
	while (*ptext) {
		if (*ptext == '"')
			inquotes = !inquotes;
		else if (!inquotes) {
			if (*ptext == *attname) {
				if (g_strncasecmp(ptext, attname, namelen) == 0
					&& (*(ptext - 1) == '"' || *(ptext - 1) == ' ')) {
					attrstart = ptext;
					ptext += namelen;
					if (*ptext && !isalnum(*ptext)) {
						while (*ptext && *ptext == ' ')
							ptext++;
						if (*ptext == '=') {
							anequal = 1;
							ptext++;
						}
						while (*ptext && *ptext == ' ')
							ptext++;
						if (*ptext == '"' || *ptext == '\'')
							endchar = *ptext;
						else
							endchar = ' ';
						pvalue = ptext;
						if (*pvalue)
							pvalue++;
						pvalue = ++ptext;
						while (*pvalue && *pvalue != endchar)
							pvalue++;
						value = g_strndup(ptext, pvalue - ptext);
						if (!anequal && endchar == ' ') {
							gchar attlist[1024] = ",";
							getDTDAttList(HTML_doctypes
										  [main_v->current_document->
										   dtd_loaded].elements,
										  attlist + 1, ",", 1024 - 1);
							g_strup(attlist);
							if (strstr(attlist, value) != NULL)
								value = NULL;
						}
						if (value) {
							if (!newvalue || !*newvalue) {	/* delete the whole attr */
								pos = startpos + (attrstart - text);
								replace_text("", pos,
											 pos + pvalue - attrstart + 1);
							} else if (strcmp(newvalue, value) != 0) {
								/* replace the attr's text, since it is different */
								pos = startpos + (ptext - text);
								replace_text(newvalue, pos,
											 pos + pvalue - ptext);
							} else {	/* The text is the same */
								modifingdocument = 0;
								return;
							}
							modifingdocument = 0;
							doc_attr_page_refresh(main_v->current_document,
												  1);
							return;
						}
					}
				}
			}
		}
		ptext++;
	}
	if (newvalue && *newvalue) {
		pvalue = g_strdup_printf(" %s=\"%s\"", attname, newvalue);
		pos = startpos + (ptext - text) - 1;
		gtk_editable_set_position(GTK_EDITABLE
								  (main_v->current_document->textbox),
								  pos);
		insert_text(pvalue);
		g_free(pvalue);
		modifingdocument = 0;
		doc_attr_page_refresh(main_v->current_document, 1);
	}							/* if no new value and no attribute found, do nothing */
}


static void attwidget_activate_lcb(GtkWidget * widget, gpointer * data)
{
	SGML_attrinfo *ai = (SGML_attrinfo *) data;
	gchar *text;
	GtkEntry *entry = (GtkEntry *) widget;

	DEBUG_MSG("Insertando el atributo %s\n", ai->name);
	text =
		gtk_editable_get_chars(GTK_EDITABLE
							   (main_v->current_document->textbox),
							   current_tag_position.start,
							   current_tag_position.end);
	set_value_in_text(ai->name, gtk_entry_get_text(entry), text,
					  current_tag_position.start);
	if (g_strcasecmp(ai->name, "class") == 0) {
	} else
		switch (ai->attrtype) {
		case HTML_AT_TEXT:		/* title, alt, standby, ... */
		case HTML_AT_URI:		/* href, src, background, ... */
		case HTML_AT_COLOR:
		case HTML_AT_LENGTH:
		case HTML_AT_PIXELS:
		case HTML_AT_MULTILENGTH:
		case HTML_AT_CONTENTYPE:
		case HTML_AT_CONTENTYPES:
		case HTML_AT_LANGUAGECODE:
		case HTML_AT_CHARSET:
		case HTML_AT_CHARSETS:
		case HTML_AT_CHARACTER:
		case HTML_AT_DATETIME:
		case HTML_AT_LYNKTYPES:
		case HTML_AT_MEDIADESC:
		case HTML_AT_SCRIPT:	/* on.... */
		case HTML_AT_STYLESHEET:	/* style */
		case HTML_AT_FRAMETARGET:	/* target */
		default:
			switch (ai->basictype) {
			case SGML_BT_CDATA:	/* name */
			case SGML_BT_NAME:
			case SGML_BT_IDREF:
			case SGML_BT_IDREFS:
			case SGML_BT_NUMBER:
			case SGML_BT_BOOLEAN:
			case SGML_BT_ID:	/* id */
			default:
			}
		}
}

/*  Function: att_add_widget_by_type
 *  Description:
 *     Adds the widget and label for the attribute number pos in the table.
 *     The code of this function is copied basically from html.c
 *     The widgets have a size of two rows, and optionally can content a button.
 *  Arguments:
 *     dgtable - The table to add the widget to
 *     ai - Info of the attribute
 *     value - value of the attribute in the text of the document
 *     pos - Number of attribute 
 *  Return:
 *     A pointer to the entry widget.
 */
static GtkWidget *att_add_widget_by_type(GtkTable * dgtable,
										 SGML_attrinfo * ai, gchar * value,
										 int pos)
{
	GtkWidget *editwidget = NULL, *optional_but;
	GList *popuplist = NULL;

	if (ai->defaultvaluetype == HTML_DEFVALUE_FIXED)
		value = ai->defaultvalue;

	pos = 1 + 2 * pos;
	//  DEBUG_MSG("Adding widget: attname=%s, attvalue=%s\n", ai->name, value);
	gtk_table_attach_defaults(dgtable, gtk_label_new(ai->name), 0, 1, pos,
							  pos + 1);
	if (g_strcasecmp(ai->name, "class") == 0) {
		editwidget =
			combo_with_popdown(value, main_v->current_project.classlist,
							   0);
		gtk_table_attach_defaults(GTK_TABLE(dgtable), editwidget, 0, 10,
								  pos + 1, pos + 2);
		editwidget = GTK_WIDGET(GTK_COMBO(editwidget)->entry);
	} else
		switch (ai->attrtype) {
		case HTML_AT_TEXT:		/* title, alt, standby, ... */
			editwidget = entry_with_text(value, 0);
			gtk_table_attach_defaults(GTK_TABLE(dgtable), editwidget, 0,
									  10, pos + 1, pos + 2);
			break;
		case HTML_AT_URI:		/* href, src, background, ... */
			editwidget =
				combo_with_popdown(value, main_v->current_project.urllist,
								   0);
			optional_but =
				file_but_new(GTK_WIDGET(GTK_COMBO(editwidget)->entry),
							 main_v->attrpage_window, 0);
			gtk_table_attach_defaults(GTK_TABLE(dgtable),
									  GTK_WIDGET(optional_but), 9, 10, pos,
									  pos + 1);
			gtk_table_attach_defaults(GTK_TABLE(dgtable),
									  GTK_WIDGET(GTK_COMBO(editwidget)), 0,
									  10, pos + 1, pos + 2);
			editwidget = GTK_WIDGET(GTK_COMBO(editwidget)->entry);
			break;
		case HTML_AT_COLOR:	/* color, bgcolor, ... */
			editwidget =
				combo_with_popdown(value,
								   main_v->current_project.colorlist, 0);
			optional_but =
				color_but_new(GTK_COMBO(editwidget)->entry,
							  main_v->attrpage_window);
			gtk_table_attach_defaults(GTK_TABLE(dgtable),
									  GTK_WIDGET(optional_but), 9, 10, pos,
									  pos + 1);
			gtk_table_attach_defaults(GTK_TABLE(dgtable),
									  GTK_WIDGET(GTK_COMBO(editwidget)), 0,
									  10, pos + 1, pos + 2);
			editwidget = GTK_WIDGET(GTK_COMBO(editwidget)->entry);
			break;
		case HTML_AT_LENGTH:	/* height, width, cellspacing, ... */
		case HTML_AT_PIXELS:	/* border, hspace, vspace */
			editwidget = spinbut_with_value(value, 0, 500, 1.0, 5.0);
			gtk_table_attach_defaults(GTK_TABLE(dgtable), editwidget, 0,
									  10, pos + 1, pos + 2);
			break;

		case HTML_AT_MULTILENGTH:	/* <COL & <COLGROUP width */
		case HTML_AT_CONTENTYPE:	/* type, enctype, accept */
		case HTML_AT_CONTENTYPES:	/* <FORM & <INPUT accept */
		case HTML_AT_LANGUAGECODE:	/* lang, xml:lang */
		case HTML_AT_CHARSET:	/* acceptkey ... */
		case HTML_AT_CHARSETS:	/* <FORM accept-targets */
		case HTML_AT_CHARACTER:	/* accesskey */
		case HTML_AT_DATETIME:	/* <INS & <DEL datetime */
		case HTML_AT_LYNKTYPES:
		case HTML_AT_MEDIADESC:	/* mediadesc */
		case HTML_AT_SCRIPT:	/* on.... */
			editwidget = entry_with_text(value, 256);
			gtk_table_attach_defaults(GTK_TABLE(dgtable), editwidget, 0,
									  10, pos + 1, pos + 2);
			break;
		case HTML_AT_STYLESHEET:	/* style */
			editwidget = entry_with_text(value, 256);
			gtk_table_attach_defaults(GTK_TABLE(dgtable), editwidget, 0,
									  10, pos + 1, pos + 2);
#ifdef NEW_CS3
			optional_but =
				style_but_new(editwidget, main_v->attrpage_window);
			gtk_table_attach_defaults(GTK_TABLE(dgtable), optional_but, 9,
									  10, pos, pos + 1);
#endif							/* #ifdef NEW_CS3 */
			break;
		case HTML_AT_FRAMETARGET:	/* target */
			editwidget =
				combo_with_popdown(value,
								   main_v->current_project.targetlist, 0);
			gtk_table_attach_defaults(GTK_TABLE(dgtable),
									  GTK_WIDGET(GTK_COMBO(editwidget)), 0,
									  10, pos + 1, pos + 2);
			editwidget = GTK_WIDGET(GTK_COMBO(editwidget)->entry);
			break;
		default:
			switch (ai->basictype) {
			case SGML_BT_BOOLEAN:
			case SGML_BT_NUMBER:
				editwidget = spinbut_with_value(value, 0, 500, 1.0, 5.0);
				gtk_table_attach_defaults(GTK_TABLE(dgtable), editwidget,
										  0, 10, pos + 1, pos + 2);
				break;
			case SGML_BT_CDATA:	/* name */
			case SGML_BT_NAME:
			case SGML_BT_IDREF:
			case SGML_BT_IDREFS:
			case SGML_BT_ID:	/* id */
				editwidget = entry_with_text(value, 256);
				gtk_table_attach_defaults(GTK_TABLE(dgtable), editwidget,
										  0, 10, pos + 1, pos + 2);
				break;
			default:
				/* The values of the attrs are in ai->othertype: (val1|val2..) */
				popuplist = get_attr_othervalues(ai);
				if( popuplist ) {
				  editwidget = combo_with_popdown(value, popuplist, 0);
				  g_list_free(popuplist);
				  gtk_table_attach_defaults(GTK_TABLE(dgtable),
							    GTK_WIDGET(GTK_COMBO
								       (editwidget)), 0,
							    10, pos + 1, pos + 2);
				  editwidget = GTK_WIDGET(GTK_COMBO(editwidget)->entry);
				} else {
					editwidget = entry_with_text(value, 256);
					gtk_table_attach_defaults(GTK_TABLE(dgtable),
								  editwidget, 0, 10, pos + 1,
								  pos + 2);
					break;
				}
			}
		}
	gtk_signal_connect(GTK_OBJECT(editwidget), "activate",
					   GTK_SIGNAL_FUNC(attwidget_activate_lcb), ai);
	return editwidget;
}

static void att_set_value_widget_by_type(SGML_attrinfo * ai, gchar * value,
										 GtkWidget * widget)
{
	GtkEntry *entry = (GtkEntry *) widget;
	gtk_entry_set_text(entry, value);
}


static void att_create_widgets(gchar * tag, GList * attrs, char *text)
{
	GList *tmplist;
	SGML_attrinfo *ai;
	GtkWidget *attwidget;
	GtkTable *atttable;
	gchar *value;
	int i, atype, rebuild;

	guint nattr = g_list_length(attrs);
	rebuild = 0;
	for (i = 0, tmplist = attrs; tmplist;
		 tmplist = g_list_next(tmplist), i++) {
		ai = (SGML_attrinfo *) tmplist->data;
		atype = (ai->attrtype == -1) ? ai->basictype : ai->attrtype;
		if (usedattrs[i] != atype) {
			rebuild = 1;
		}
	}
	atttable = (GtkTable *) GTK_BIN(main_v->attrpage_window)->child;
	if (rebuild) {
		if (atttable)
			gtk_object_destroy(GTK_OBJECT(atttable));
		atttable = (GtkTable *) gtk_table_new(nattr * 2, 10, 0);
	}

	DEBUG_MSG("needs to be rebuilt=%d\n", rebuild);

	if (rebuild)
		gtk_table_attach_defaults(atttable, gtk_label_new(tag), 0, 1, 0,
								  1);
	for (nusedwidgets = 0, tmplist = attrs; tmplist;
		 tmplist = g_list_next(tmplist), nusedwidgets++) {
		ai = (SGML_attrinfo *) tmplist->data;
		if (ai) {
			value = get_value_from_text(ai->name, text);
			if (rebuild) {
				attwidget =
					att_add_widget_by_type(atttable, ai, value,
										   nusedwidgets);
				usedwidgets[nusedwidgets] = attwidget;
				usedattrs[nusedwidgets] =
					(ai->attrtype == -1) ? ai->basictype : ai->attrtype;
			} else {
				if (value)
					att_set_value_widget_by_type(ai, value,
												 usedwidgets
												 [nusedwidgets]);
				else
					att_set_value_widget_by_type(ai, "",
												 usedwidgets
												 [nusedwidgets]);
			}
			if (value)
				g_free(value);
		}
	}
	if (rebuild) {
		gtk_widget_show(GTK_WIDGET(atttable));
		gtk_scrolled_window_add_with_viewport((GtkScrolledWindow *)
											  main_v->attrpage_window,
											  (GtkWidget *) atttable);
	}
}


static gint att_refresh_tag_position(gint newposition)
{
	if (current_tag_position.start <= newposition
		&& current_tag_position.end >= newposition)
		return FALSE;
	else
		return TRUE;
}


void att_page_refresh(GList * elements, gint forced, gint newposition)
{
	gchar *text, *ptext;
	gchar tag[200], *ptag = tag;
	SGML_elementinfo *ei;

	if (modifingdocument)
		return;

	DEBUG_MSG("att_page_refresh: forced=%d, newposition=%d\n", forced,
			  newposition);
	DEBUG_MSG("att_page_refresh: cur.start=%d, cur.end=%d\n",
			  current_tag_position.start, current_tag_position.end);
	if (forced || att_refresh_tag_position(newposition)) {
		text = get_text_of_tag(newposition, &current_tag_position);
		if (text) {
			ptext = text;
			while (*ptext && *ptext == ' ')
				ptext++;
			while (*ptext && *ptext != ' ')
				*ptag++ = *ptext++;
			*ptag = '\0';
			DEBUG_MSG("att_page_refresh: tag=%s\n", tag);
			ei = get_element_byname(elements, tag);
			if (ei) {
				att_create_widgets(tag, ei->attrs, ptext);
				gtk_widget_show_all(GTK_WIDGET(main_v->attrpage_window));
			} else {
				gtk_widget_hide(main_v->attrpage_window);
			}
			g_free(text);
		} else {
			gtk_widget_hide(main_v->attrpage_window);
		}
	}
}



#endif
