/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  Copyright (C) 2003 Takuro Ashie
 *  Copyright (C) 2004 Hiroyuki Ikezoe
 *
 *  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.
 */

#define _XOPEN_SOURCE
#define _BSD_SOURCE

#include <string.h>
#include <time.h>

#include "kz-rss.h"
#include "kz-xml.h"

#include <string.h>
#include <stdlib.h>
#include <time.h>

static gboolean kz_rss_is_supported      (KzBookmarkFile *bookmark,
					  const gchar    *buffer);
static gboolean kz_rss_parse_from_string (KzBookmarkFile *bookmark,
					  const gchar    *buffer,
					  guint           length,
					  GError        **error);
static gchar   *kz_rss_get_encoding      (gchar          *src);
static gchar   *kz_rss_convert_to_utf8   (gchar          *src,
					  guint           src_len,
					  gsize          *dest_len);
static void     kz_rss_parse_xml         (KzBookmark     *bookmark, 
					  KzXML          *xml);

static KzBookmarkFileType rss_file_type =
{
	/* .priority_hint = */ 0,
	/* .file_type     = */ "RSS",
	/* .init          = */ NULL,
	/* .is_supported  = */ kz_rss_is_supported,
	/* .from_string   = */ kz_rss_parse_from_string,
	/* .to_string     = */ NULL,
};


KzBookmarkFileType *
kz_rss_get_file_types (gint idx)
{
	if (idx == 0)
		return &rss_file_type;
	else
		return NULL;
}


static gboolean
kz_rss_is_supported(KzBookmarkFile *bookmark, const gchar *buf)
{
	const gchar *pos;

	g_return_val_if_fail(buf, FALSE);

	if (!g_str_has_prefix(buf, "<?xml")) return FALSE;

	pos = buf;

	/* find first element */
	do
		pos = strchr(pos + 1, '<');
	while (pos && (pos[1] == '!' || pos[1] == '?'));

	if (!pos) return FALSE;

	if (g_str_has_prefix(pos, "<rdf") || g_str_has_prefix(pos, "<rss"))
		return TRUE;

	return FALSE;
}


static gboolean
kz_rss_parse_from_string (KzBookmarkFile *bookmark,
			  const gchar *buffer, guint length,
			  GError **error)
{
	KzXML *xml;
	gchar *utf8_body = NULL;
	gsize utf8_len;

	g_return_val_if_fail(KZ_IS_BOOKMARK_FILE(bookmark), FALSE);

	g_return_val_if_fail(buffer != NULL, FALSE);

	if (length < 0)
		length = strlen(buffer);

	/* check encoding and convert if need */
	utf8_body = kz_rss_convert_to_utf8((gchar*)buffer, length, &utf8_len);

	xml = kz_xml_new();

	if (!kz_xml_load_xml(xml, utf8_body, utf8_len))
	{
		g_free(utf8_body);
		return FALSE;
	}

	kz_rss_parse_xml(KZ_BOOKMARK(bookmark), xml);
	g_object_unref(xml);

	g_free(utf8_body);

	return TRUE;
}


static void
parse_item_node (KzXMLNode *parent,
		 KzBookmark *bookmark)
{
	KzXMLNode *node;
	KzBookmark *child;

	g_return_if_fail(KZ_IS_BOOKMARK(bookmark));

	if (!kz_xml_node_is_element(parent)) return;

	child = kz_bookmark_new();
	for (node = kz_xml_node_first_child(parent);
	     node;
	     node = kz_xml_node_next(node))
	{
		if (!kz_xml_node_is_element(node)) continue;

		if (kz_xml_node_name_is(node, "title"))
		{
			gchar *title = kz_xml_node_to_str(node);
			kz_bookmark_set_title(child, title);
			g_free(title);
		}
		else if (kz_xml_node_name_is(node, "link"))
		{
			gchar *link = kz_xml_node_to_str(node);
			kz_bookmark_set_link(child, link);
			g_free(link);
		}
		else if (kz_xml_node_name_is(node, "description"))
		{
			gchar *desc = kz_xml_node_to_str(node);
			kz_bookmark_set_description(child, desc);
			g_free(desc);
		}
		else if (kz_xml_node_name_is(node, "dc:date"))
		{
			struct tm t = {0};
			gchar *date = kz_xml_node_to_str(node);
			strptime(date,
				 "%Y-%m-%dT%H:%M:%S", &t);
			kz_bookmark_set_last_modified(child, (guint)mktime(&t));
			g_free(date);
		}
		else
		{
			/*g_warning("This element does not support!");*/
		}
	}

	kz_bookmark_append(bookmark, child);
	g_object_unref(G_OBJECT(child));
}


static void
parse_channel_node (KzXMLNode *parent,
		    KzBookmark *bookmark)
{
	KzXMLNode *node;

	g_return_if_fail(KZ_IS_BOOKMARK(bookmark));

	if (!kz_xml_node_is_element(parent)) return;

	for (node = kz_xml_node_first_child(parent);
	     node;
	     node = kz_xml_node_next(node))
	{
		if (!kz_xml_node_is_element(node)) continue;

		if (kz_xml_node_name_is(node, "title"))
		{
			gchar *title = kz_xml_node_to_str(node);
			kz_bookmark_set_document_title(bookmark, title);
			g_free(title);
		}
		else if (kz_xml_node_name_is(node, "link"))
		{
			gchar *link = kz_xml_node_to_str(node);
			kz_bookmark_set_link(bookmark, link);
			g_free(link);
		}
		else if (kz_xml_node_name_is(node, "description"))
		{
			gchar *desc = kz_xml_node_to_str(node);
			kz_bookmark_set_description(bookmark, desc);
			g_free(desc);
		}
		else if (kz_xml_node_name_is(node, "item"))
		{
			parse_item_node(node, bookmark);
		}
		else if (kz_xml_node_name_is(node, "dc:date"))
		{
			struct tm t = {0};
			gchar *date = kz_xml_node_to_str(node);
			strptime(date,
				 "%Y-%m-%dT%H:%M:%S", &t);
			kz_bookmark_set_last_modified(bookmark, (guint)mktime(&t));
			g_free(date);
		}
		else
		{
			/*g_warning("This element does not support!");*/
		}
	}
}


static void
kz_rss_parse_xml (KzBookmark *bookmark, 
		  KzXML *xml)
{
	KzXMLNode *node, *root_node;

	g_return_if_fail(KZ_IS_BOOKMARK(bookmark));

	root_node = kz_xml_get_root_element (xml);
	if (!root_node) return;
	g_return_if_fail (kz_xml_node_name_is(root_node, "rdf:RDF") || 
			  kz_xml_node_name_is(root_node, "rss"));

	for (node = kz_xml_node_first_child(root_node);
	     node;
	     node = kz_xml_node_next(node))
	{
		if (!kz_xml_node_is_element(node)) continue;

		if (kz_xml_node_name_is(node, "channel"))
		{
			parse_channel_node(node, bookmark);
		}
		else if (kz_xml_node_name_is(node, "item"))
		{
			parse_item_node(node, bookmark);
		}
	}
}


static gchar *
kz_rss_get_encoding (gchar *src)
{
	gchar *encoding = NULL;
	gchar *xml_element;
	gchar *prev_pos, *next_pos;
	
	g_return_val_if_fail(src, NULL);

	next_pos = strstr(src, "\n");
	
	if (!next_pos)
		return NULL;
	
	xml_element = g_strndup(src, next_pos - src);
	
	prev_pos =  strstr(xml_element, "encoding=");
	if (prev_pos)
	{
		while (*prev_pos != '"' && *prev_pos != '\0')
		{
			prev_pos++;
		}
		if (prev_pos != '\0')
		{
			prev_pos++;
			next_pos = strchr(prev_pos, '"');
			if (next_pos)
				encoding = g_strndup(prev_pos, 
						     next_pos - prev_pos);
			if (encoding && !strcasecmp(encoding, "UTF-8"))
			{
				g_free(encoding);
				encoding = NULL;
			}
		}
	}

	g_free(xml_element);
	return encoding;
}

static gchar *
kz_rss_convert_to_utf8 (gchar *src, guint src_len, gsize *dest_len)
{
	gchar *dest = NULL;
	gchar *encoding = NULL;
	gsize read_len;

	encoding = kz_rss_get_encoding(src);

	if (encoding)
	{
		dest = g_convert(src, src_len, "UTF-8", encoding,
				 &read_len, dest_len, NULL);
		g_free(encoding);
		
		/* If converting fails, return original */
		if (!dest)
			dest = g_strndup(src, src_len);
	}
	else
	{
		dest = g_strndup(src, src_len);
		*dest_len = src_len;
	}

	return dest;
}
