/* 
 *  Copyright (C) 2002 Ricardo Fernández Pascual
 *
 *  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, 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.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

/* This file is based on the original netscape.c file of galeon 1.x
 *
 * Netscape bookmark importing by Nate Case <nd@kracked.com>
 * Netscape bookmark exporting by Ricardo Fernández Pascual <ric@users.sourceforge.net>
 */

#include "bookmarks-netscape-mozilla.h"
#include "gul-string.h"
#include "gul-general.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glib/gi18n.h>

/* local data types */

/**
 * NSItemType: netscape bookmark item type
 */
typedef enum
{
	NS_SITE,
	NS_NOTES,
	NS_FOLDER,
	NS_FOLDER_END,
	NS_SEPARATOR,
	NS_ALIAS,
	NS_UNKNOWN
} NSItemType;


/* local function prototypes */
static NSItemType		ns_get_bookmark_item		(FILE *f, GString *name, GString *url,
								 GString *nick, GString* alias_id,
								 GTime *added, GTime *visited);
static GTime 			ns_get_bookmark_date		(const char *line, const char *search);
static char *			ns_parse_bookmark_item		(GString *string);

static void			netscape_export_bookmarks_item	(FILE *file, GbBookmark *b, 
								 gboolean use_locale);
static gchar * 			ns_locale_to_utf8		(const gchar *locstr);	
static gchar *			ns_utf8_to_locale		(const gchar *locstr);


/**
 * Reads a file and returns the bookmarks in it
 */
GbBookmarkSet *
netscape_import_bookmarks (const gchar *filename, gboolean use_locale)
{
	GbBookmark *newbm = NULL;
	GbFolder *parent;
	GbBookmarkSet *ret;
	FILE *bf;  /* bookmark file */
	GString *name = g_string_new (NULL);
	gchar *parsedname;
	GString *url = g_string_new (NULL);
	GString *nick = g_string_new (NULL);
	GString *alias_id = g_string_new (NULL);
	GTime added, visited;
	gchar *str;
	gchar * (*to_utf8) (const gchar *) = 
		use_locale ? ns_locale_to_utf8 : g_strdup;
	
	if (!(bf = fopen (filename, "r"))) {
		g_warning ("Failed to open file: %s\n", filename);
		return NULL;
	}	
	
	ret = gb_bookmark_set_new ();
	parent = gb_folder_new (ret, _("Mozilla bookmarks"));
	gb_bookmark_set_set_root (ret, parent);

	while (!feof (bf)) {
		NSItemType t;
		added = visited = 0;
		t = ns_get_bookmark_item (bf, name, url, nick, alias_id, &added, &visited );
		switch (t)
		{
		case NS_FOLDER: 
			str = to_utf8 (name->str);
			newbm = (GbBookmark *) gb_folder_new (ret, str);
			g_free (str);
			if (!strcmp (name->str, _("Personal Toolbar Folder")))
			{
				gb_folder_set_create_toolbar (GB_FOLDER (newbm), TRUE);
			}
			gb_folder_add_child (parent, newbm, -1);
			gb_bookmark_set_time_added (newbm, added);
			parent = (GbFolder *) newbm;
			break;
		case NS_SITE:
			parsedname = ns_parse_bookmark_item (name);
			str = to_utf8 (parsedname);
			g_free (parsedname);
			if (strstr (url->str, "%s"))
			{
				/* Smart bookmark - bug 136069 */
				newbm = (GbBookmark*) gb_smart_site_new (ret, str, NULL, url->str);
			}
			else
			{
				newbm = (GbBookmark*) gb_site_new (ret, str, url->str);
			}
			g_free (str);

			if (alias_id->str && strlen( alias_id->str ))
			{
				str = g_strconcat ("NSAlias", alias_id->str, NULL);
				gb_bookmark_set_id (newbm, str);
				g_free (str);
			}

			if (nick->str) 
			{
				str = to_utf8 (nick->str);
				gb_bookmark_set_nick (newbm, str);
				g_free (str);
			}
			gb_folder_add_child (parent, newbm, -1);
			gb_bookmark_set_time_added (newbm, added);
			gb_site_set_time_visited ((GbSite *) newbm, visited);
			break;
		case NS_ALIAS:
			str = g_strconcat ("NSAlias", alias_id->str, NULL);
			newbm = (GbBookmark*) gb_alias_placeholder_new (ret, str);
			g_free (str);
			gb_folder_add_child (parent, newbm, -1);

		case NS_NOTES:
			if (newbm && !GB_IS_ALIAS_PLACEHOLDER (newbm))
			{
				gchar *notes;
				str = to_utf8 (name->str);
				notes = g_strconcat (newbm->notes, str, NULL);
				gb_bookmark_set_notes (newbm, notes);
				g_free (notes);
				g_free (str);
			}
			break;
		case NS_SEPARATOR:
			newbm = (GbBookmark *) gb_separator_new (ret);
			gb_folder_add_child (parent, newbm, -1);
			newbm = NULL;
			break;
		case NS_FOLDER_END:
			newbm = NULL;
			if (GB_BOOKMARK (parent)->parent != NULL)
			{
				parent = GB_BOOKMARK (parent)->parent;
			}
			break;
		default:
			newbm = NULL;
			break;
		}		
	}
	fclose (bf);
	g_string_free (name, TRUE);
	g_string_free (url, TRUE);
	g_string_free (nick, TRUE);
	g_string_free (alias_id, TRUE);

	gb_bookmark_set_resolve_aliases (ret);
	
	return ret;
}

static GTime
ns_get_bookmark_date (const char *line, const char *search)
{
	const char *found = gul_string_ascii_strcasestr (line, search);
	if (!found)
	{
		return 0;
	}
	else
	{
		return atoi (found + strlen (search) + 1);
	}
}

/** 
 * Parses a line of a mozilla/netscape bookmark file. File must be open.
 */
/* this has been tested fairly well */
static NSItemType 
ns_get_bookmark_item (FILE *f, GString *name, GString *url, GString *nick,
		      GString* alias_id, GTime *added, GTime *visited)
{
	char *line = NULL;
	char *found;

	line = gul_general_read_line_from_file (f);

	if ((found = (char *) gul_string_ascii_strcasestr (line, "<A HREF="))) 
	{  /* declare site? */
		g_string_assign (url, found+9);  /* url=URL+ ADD_DATE ... */
		g_string_truncate (url, strstr(url->str, "\"")-url->str);
		found = (char *) strstr (found+9+url->len, "\">");
		if (!found) 
		{
			g_free (line);
			return NS_UNKNOWN;
		}
		g_string_assign (name, found+2);
		g_string_truncate (name, gul_string_ascii_strcasestr (name->str, 
						       "</A>")-name->str);
		if ((found = (char *) gul_string_ascii_strcasestr (line, 
							"SHORTCUTURL=")))
		{
			g_string_assign (nick, found+13);
			g_string_truncate (nick, strstr(nick->str, "\"")
					   - nick->str);
		} 
		else
		{
			g_string_assign (nick, "");
		}
		*added = ns_get_bookmark_date (line, "ADD_DATE=");
		*visited = ns_get_bookmark_date (line, "LAST_VISIT=");

		g_string_assign (alias_id, "");
		if ((found = (char *) gul_string_ascii_strcasestr (line, "ALIASID=")) ||
		    (found = (char *) gul_string_ascii_strcasestr (line, "ALIASOF=")))
		{
			g_string_assign (alias_id, found+9);
			g_string_truncate (alias_id, strstr(alias_id->str, "\"")
					   - alias_id->str);
			if (found[5] == 'O' || found[5] == 'o')
			{
				g_free (line);
				return NS_ALIAS;
			}
		}
		g_free (line);
		return NS_SITE;
	} 
	else if ((found = (char *) gul_string_ascii_strcasestr (line, "<DT><H3"))) 
	{ /* declare folder? */
		found = (char *) strstr(found+7, ">");
		if (!found) return NS_UNKNOWN;
		g_string_assign (name, found+1);
		g_string_truncate (name, gul_string_ascii_strcasestr (name->str,
				   "</H3>") - name->str);
		*added = ns_get_bookmark_date (line, "ADD_DATE=");
		g_free (line);
		return NS_FOLDER;		
	} 
	else if ((found = (char *) gul_string_ascii_strcasestr (line, "</DL>"))) 
	{     /* end folder? */
		g_free (line);
		return NS_FOLDER_END;
	}
	else if ((found = (char *) gul_string_ascii_strcasestr (line, "<HR>"))) 
	{    /* separator */
		g_free (line);
		return NS_SEPARATOR;
	}

	/* Now look for comments - remove any trailing <br> - bug 125904*/
	if ((found = (char *) gul_string_ascii_strcasestr (line, "<BR>\n")))
	{
		found[0] = '\n';
		found[1] = '\0';
	}

	if ((found = (char *) gul_string_ascii_strcasestr (line, "<DD>"))) 
	{    /* comments */
		g_string_assign (name, found+4);
		g_free (line);
		return NS_NOTES;
	}
	else if (strchr(line, '<')==NULL && strchr(line, '>')==NULL)
	{    /* continued comments (probably) */
		g_string_assign (name, line);
		g_free (line);
		return NS_NOTES;
	}
	g_free (line);
	return NS_UNKNOWN;
}

/**
 * This function replaces some weird elements
 * like &amp; &le;, etc..
 * More info : http://www.w3.org/TR/html4/charset.html#h-5.3.2
 * NOTE : We don't support &#D or &#xH.
 * Patch courtesy of Almer S. Tigelaar <almer1@dds.nl>
 */
static char *
ns_parse_bookmark_item (GString *string)
{
	char *iterator, *temp;
	int cnt = 0;
	GString *result = g_string_new (NULL);

	g_return_val_if_fail (string != NULL, NULL);
	g_return_val_if_fail (string->str != NULL, NULL);

	iterator = string->str;
	
	for (cnt = 0, iterator = string->str;
	     cnt <= (int)(strlen (string->str));
	     cnt++, iterator++) {
		if (*iterator == '&') {
			int jump = 0;
			int i;

			if (g_ascii_strncasecmp (iterator, "&amp;", 5) == 0) {
			
				g_string_append_c (result, '&');
				jump = 5;
			} else if (g_ascii_strncasecmp (iterator, "&lt;", 4) == 0) {
			
				g_string_append_c (result, '<');
				jump = 4;
			} else if (g_ascii_strncasecmp (iterator, "&gt;", 4) == 0) {

				g_string_append_c (result, '>');
				jump = 4;
			} else if (g_ascii_strncasecmp (iterator, "&quot;", 6) == 0) {
			
				g_string_append_c (result, '\"');
				jump = 6;
			} else {
				/* It must be some numeric thing now */

				iterator++;

				if (iterator && *iterator == '#') {
					int val;
					char *num, *tmp;
					
					iterator++;
					
					val = atoi (iterator);

					tmp = g_strdup_printf ("%d", val);
					jump = strlen (tmp);
					g_free (tmp);
					
					num = g_strdup_printf ("%c", (char) val);
					g_string_append (result, num);
					g_free (num);
				}
			}
			for (i = jump - 1; i > 0; i--) {
				iterator++;
				if (iterator == NULL)
					break;
			}
		} else 
			g_string_append_c (result, *iterator);
	}
	temp = result->str;
	g_string_free (result, FALSE);
	return temp;
}

/**
 * Exports bookmarks to netscape or mozilla
 * Will overwrite the file!
 */
gboolean
netscape_export_bookmarks (const gchar *filename, GbBookmarkSet *set, 
			   gboolean use_locale)
{
	FILE *file;
	GbBookmark *it;

	g_return_val_if_fail (filename != NULL, FALSE);

	if (!(file = fopen (filename, "w"))) 
	{
		g_warning (_("Could not open file: %s"), filename);
		return FALSE;
	}

	/* write the headers */
	fputs ("<!DOCTYPE NETSCAPE-Bookmark-file-1>\n", file);
	fputs ("<!-- This file was automatically generated by Galeon\n", file);
	fputs ("It will be read and overwritten.\n", file);
	fputs ("Do Not Edit! -->\n", file);
	if (!use_locale)
	{
		fputs ("<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html;"
		       " charset=UTF-8\">\n", file);
	}
	fputs ("<TITLE>Bookmarks</TITLE>\n", file);
	fputs ("<H1>Bookmarks</H1>\n", file);
	fputs ("\n", file);
	fputs ("<DL><p>\n", file);

	/* write the bookmarks */
	for (it = set->root->child; it; it = it->next) 
	{
		netscape_export_bookmarks_item (file, it, use_locale);
	}
	
	/* finish */
	fputs ("</DL><p>\n", file);

	fclose (file);	
	return TRUE;
}

static void 
netscape_export_bookmarks_item (FILE *file, GbBookmark *b, gboolean use_locale)
{
	gchar *str, *str1;
	gchar * (*from_utf8) (const gchar *) = 
		use_locale ? ns_utf8_to_locale : g_strdup;
	

	if (GB_IS_SITE (b))
	{
		fputs ("\t<DT><A HREF=\"", file);
		str = gul_string_strdup_replace (GB_SITE (b)->url, "\"", "&quot;");
		fputs (str, file);
		g_free (str);
		fputs ("\"", file);
		if (b->time_added > 0)
		{
			fprintf (file, " ADD_DATE=\"%d\"", b->time_added);
		}
		if (b->time_modified > 0) 
		{
			fprintf (file, " LAST_MODIFIED=\"%d\"", b->time_modified);
		}
		if (GB_SITE (b)->time_visited > 0)
		{
			fprintf (file, " LAST_VISIT=\"%d\"", GB_SITE (b)->time_visited);
		}
		/* support a mozilla-only feature, but won't harm netscape */
		if (strlen (b->nick) > 0) {
			fputs (" SHORTCUTURL=\"", file);
			str = from_utf8 (b->nick);
			fputs (str, file);
			g_free (str);
			fputs ("\"", file);
		}
		fputs (">", file);
		str1 = g_markup_escape_text (b->name, -1);
		str = from_utf8 (str1);
		fputs (str, file);
		g_free (str);
		g_free (str1);
		fputs ("</A>\n", file);
		if (strlen (b->notes) > 0) {
			fputs ("<DD>", file);
			str = from_utf8 (b->notes);
			fputs (str, file);
			g_free (str);
			fputs ("\n", file);
		}
	} 
	else if (GB_IS_FOLDER (b))
	{
		fputs ("<DT><H3 ADD_DATE=\"0\">", file); 
		str1 = g_markup_escape_text (b->name, -1);
		str = from_utf8 (str1);
		fputs (str, file);
		g_free (str);
		g_free (str1);
		fputs ("</H3>\n", file);
		if (strlen (b->notes) > 0) {
			fputs ("<DD>", file);
			str = from_utf8 (b->notes);
			fputs (str, file);
			g_free (str);
			fputs ("\n", file);
		}
		fputs ("<DL><p>\n", file);
		/* FIXME: Alias catogories are not exported */
		if (!b->alias_of)
		{
			GbBookmark *it;
			for (it = GB_FOLDER (b)->child; it != NULL; it = it->next) 
			{
				netscape_export_bookmarks_item (file, it, use_locale);
			}
		}
		else
		{
			g_warning ("Detected a bookmark item type that I can't export!");
		}
		fputs ("</DL><p>\n", file);
	}
	else if (GB_IS_SEPARATOR (b))
	{
		fputs ("<HR>\n", file);
	}
	else
	{
		g_warning ("Detected a bookmark item type that I can't export!");
	}
}

static gchar *
ns_locale_to_utf8 (const gchar *locstr)
{
	gchar *ret = g_locale_to_utf8 (locstr, -1, NULL, NULL, NULL);
	return ret ? ret : g_strdup ("");
}

static gchar *
ns_utf8_to_locale (const gchar *locstr)
{
	gchar *ret = g_locale_from_utf8 (locstr, -1, NULL, NULL, NULL);
	return ret ? ret : g_strdup ("");
}



