/* -*- 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.
 */

#include <string.h>
#define __USE_XOPEN
#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      (KzBookmark  *bookmark,
					  const gchar *buffer);
static gboolean kz_rss_parse_from_string (KzBookmark  *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(KzBookmark *bookmark, const gchar *buf)
{
	const gchar *pos;

	g_return_val_if_fail(buf, FALSE);

	if (strncmp(buf, "<?xml", 5)) return FALSE;

	pos = buf;

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

	if (!pos) return FALSE;

	if (!strncmp(pos, "<rdf",4) || !strncmp(pos, "<rss",4))
		return TRUE;

	return FALSE;
}


static gboolean
kz_rss_parse_from_string (KzBookmark *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(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(bookmark, 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;
			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;
			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;
}
