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

/*
 *  Copyright (C) 2006 Ryo SHIMIZU
 *
 *  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>
#include <ctype.h>
#include <glib/gi18n.h>

#include "kazehakase.h"
#include "utils/utils.h"
#include "glib-utils.h"
#include "rast-search.h"
#include "egg-pixbuf-thumbnail.h"

#define HISTORY_INDEX "/."PACKAGE"/history_index.rast"

#define RAST_URI "http://projects.netlab.jp/rast/"
#define DTD   "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">"
#define HEAD  "<head>\n" \
	      "  <title>Full-text search in history</title>\n" \
	      "  <link rel=\"stylesheet\" type=\"text/css\" href=\"history-search:?css=search-result.css\">\n" \
	      "</head>\n"
#define HEADER  ""
#define CONTENT "<div class=\"content\">\n" \
	        "  <div class=\"header\"><span class=\"title\"><a href=\"%s\">%s</a></span></div>\n" \
		"    <div class=\"summary\"><img src=\"%s\" class=\"thumbnail\">\n" \
		"    <span class=\"sentence\">%s</span>\n" \
		"  </div>\n" \
		"  <div class=\"footer\">\n" \
		"    <span class=\"uri\">%s</span>\n" \
		"    <span class=\"cache\"><a href=\"%s\">cache</a></span>\n" \
		"    <span class=\"date\">%s</span>\n" \
		"  </div>\n" \
		"</div>\n"
#define FOOTER  "<div class=\"footer\">\n" \
	        "Powered by <a href=\"%s\">Rast</a> version %s\n" \
		"</div>\n"

static gchar *rast_get_version (void);
gchar *get_value (const gchar *line);

static gboolean
rast_execute_search_command(const gchar *search_text, gint *standard_output)
{
	gboolean ret;
	const gchar *rast_cmd = "rast search ";
	gchar *command;
	gint argc;
	gchar **argv = NULL;
	GSpawnFlags flags;
	GPid pid;
	gint err;
	gchar **split = NULL;
	gchar *join = NULL;
	gint max_results = 20, num_summary = 128;
	gchar *except_word;
	gchar **except_keywords = NULL;

	KZ_CONF_GET("History", "num_summary", num_summary, INT);
	KZ_CONF_GET("History", "max_results", max_results, INT);

	split = g_strsplit(search_text, " ", -1);
	if (split)
	{
		join = g_strjoinv(" & ", split);
		g_strfreev(split);
	}

	except_word = KZ_CONF_GET_STR("History", "except_keyword");
	if (except_word && *except_word)
	{
		except_keywords = g_strsplit(except_word, ",", -1);
		g_free(except_word);
		except_word = g_strjoinv(" - ", except_keywords);
		g_strfreev(except_keywords);

		command = g_strdup_printf("%s --num-items %d --summary-nchars %d '%s - %s' %s%s ",
					  rast_cmd,
					  max_results, 
					  num_summary,
					  join,
					  except_word,
					  g_get_home_dir(),
					  HISTORY_INDEX);
		g_free(except_word);
	}
	else
	{
		command = g_strdup_printf("%s --num-items %d --summary-nchars %d '%s' %s%s",
					  rast_cmd,
					  max_results, 
					  num_summary,
					  join,
					  g_get_home_dir(),
					  HISTORY_INDEX" ");
	}

	if (join)
		g_free(join);

	g_shell_parse_argv(command,
			   &argc,
			   &argv,
			   NULL);

	flags = G_SPAWN_SEARCH_PATH; 
	ret = g_spawn_async_with_pipes(NULL,
				       argv,
				       NULL,
				       flags,
				       NULL,
				       NULL,
				       &pid,
				       NULL,
				       standard_output,
				       &err,
				       NULL);
	g_strfreev(argv);
	g_free(command);

	return ret;
}


static gchar *
rast_create_search_result_html (gint out, const gchar *text)
{
	GIOChannel *io;
	gchar *line;
	gsize length;
	gchar *title = NULL, *uri = NULL, *date = NULL, *desc = NULL;
	gchar *cache_link = NULL;
	gchar *rastversion = rast_get_version();
	GString *html;

	io = g_io_channel_unix_new(out);
	g_io_channel_set_encoding(io, NULL, NULL);
	
	html = g_string_sized_new(0);

	g_string_append(html, DTD"\n");
	g_string_append(html, "<html>\n");
	g_string_append(html, HEAD);
	g_string_append(html, "<body>\n");

	g_string_append_printf(html, "<h1>Search results for %s</h1>",
			       text);

	while (g_io_channel_read_line(io, &line, &length, NULL, NULL) == G_IO_STATUS_NORMAL)
	{
		if (g_str_has_prefix(line, "uri :"))
		{
			size_t len;
			gchar *dirname = g_strconcat(g_get_home_dir(), 
						     HISTORY_DIR,
						     NULL);
			len = strlen(dirname);

			cache_link = get_value(line);
			g_print("%s\n", cache_link);
			g_print("%s\n", dirname);
			uri = create_uri_from_filename(cache_link + 
						       strlen("file://") + 
						       len);
			g_free(dirname);
		}
		else if (g_str_has_prefix(line, "summary :"))
		{
			gchar *thumb_filename, *thumb_uri;
			gchar *summary = get_value(line);

			desc = remove_tag(summary, g_strlen(summary));
			thumb_filename = egg_pixbuf_get_thumb_filename(uri,
								       EGG_PIXBUF_THUMB_LARGE);
			thumb_uri = g_strdup_printf("history-search:?image=%s",
						    thumb_filename);
			g_string_append_printf(html,
					       CONTENT,
					       uri,
					       title,
					       thumb_uri, /* thumbnail */
					       desc,
					       uri,
					       cache_link,
					       date);

			g_free(desc);
			g_free(title);
			g_free(uri);
			g_free(date);
			g_free(cache_link);
			g_free(summary);
			g_free(thumb_filename);
			g_free(thumb_uri);
		}
		else if (g_str_has_prefix(line, "title :"))
		{
			title = get_value(line);
		}
		else if (g_str_has_prefix(line, "last_modified :"))
		{
			date = get_value(line);
		}
		g_free(line);
	}
	g_io_channel_unref(io);
	g_string_append_printf(html, FOOTER, RAST_URI, rastversion);
	g_string_append(html, "</body></html>");

	if (rastversion)
		g_free(rastversion);
	return g_string_free(html, FALSE);
}


gchar *
rast_get_search_result (const gchar *text)
{
	gint out;

	if (!text) return NULL;
	if (!exists_search_cmd) return NULL;

	if (!rast_execute_search_command(text, &out))
		return NULL;

	return rast_create_search_result_html(out, text);
}

gboolean
rast_update_index (gpointer data)
{
	const gchar *rast_register = "rast register ";
	gchar *index_dir;
	gchar *command;
	gint argc;
	gchar **argv = NULL;
	GSpawnFlags flags;
	GPid pid;

	index_dir = g_strconcat(g_get_home_dir(), HISTORY_INDEX, NULL);

	command = g_strconcat(rast_register,
			      index_dir,
			      " ",
			      (gchar*)data,
			      NULL);
	g_free(index_dir);

	g_shell_parse_argv(command,
			   &argc,
			   &argv,
			   NULL);

	flags = G_SPAWN_SEARCH_PATH |
		G_SPAWN_STDOUT_TO_DEV_NULL;
	g_spawn_async(NULL,
		      argv,
		      NULL,
		      flags,
		      NULL,
		      NULL,
		      &pid,
		      NULL);
	g_strfreev(argv);
	g_free(command);

	g_free(data);
	
	return FALSE;
}


gboolean
rast_purge_index (void)
{
	const gchar *estpurge = "rast delete";
	gchar *command;
	gint argc;
	gchar **argv = NULL;
	GSpawnFlags flags;
	GPid pid;

	/* purge index */
	flags = G_SPAWN_SEARCH_PATH |
		G_SPAWN_STDOUT_TO_DEV_NULL;
	command = g_strconcat(estpurge,
			      g_get_home_dir(),
			      HISTORY_INDEX,
			      NULL);

	g_shell_parse_argv(command,
			   &argc,
			   &argv,
			   NULL);
	flags = G_SPAWN_SEARCH_PATH |
		G_SPAWN_STDOUT_TO_DEV_NULL;
	
	g_spawn_async(NULL,
		      argv,
		      NULL,
		      flags,
		      NULL,
		      NULL,
		      &pid,
		      NULL);

	g_strfreev(argv);
	g_free(command);

	return FALSE;
}

GPid
rast_optimize_index (void)
{
	const gchar *estoptimize = "rast optimize "; 
	gchar *command;
	gint argc;
	gchar **argv = NULL;
	GSpawnFlags flags;
	GPid pid;
	
	/* optimize index process */
	command = g_strconcat(estoptimize, 
			      g_get_home_dir(), 
			      HISTORY_INDEX,
			      NULL);

	g_shell_parse_argv(command,
			   &argc,
			   &argv,
			   NULL);
	flags = G_SPAWN_SEARCH_PATH |
		G_SPAWN_STDOUT_TO_DEV_NULL;
	
	g_spawn_async(NULL,
		      argv,
		      NULL,
		      flags,
		      NULL,
		      NULL,
		      &pid,
		      NULL);
	g_strfreev(argv);
	g_free(command);

	return pid;
}

static gchar*
rast_get_version (void)
{
	gchar *version;
	const gchar *rastversion = "rast-config --version";
	gint argc;
	gchar **argv = NULL;
	GSpawnFlags flags;
	GPid pid;
	gint out, err;
	gboolean ret;
	GIOChannel *io;
	gsize length;

	if (!exists_search_cmd) return NULL;
	
	g_shell_parse_argv(rastversion,
			   &argc,
			   &argv,
			   NULL);

	flags = G_SPAWN_SEARCH_PATH; 
	ret = g_spawn_async_with_pipes(NULL,
				       argv,
				       NULL,
				       flags,
				       NULL,
				       NULL,
				       &pid,
				       NULL,
				       &out,
				       &err,
				       NULL);
	g_strfreev(argv);
	if (!ret) return NULL;

	io = g_io_channel_unix_new(out);
	g_io_channel_set_encoding(io, NULL, NULL);
	g_io_channel_read_line(io, &version, &length, NULL, NULL);
	g_io_channel_shutdown(io, TRUE, NULL);
	g_io_channel_unref(io);

	return version;
}

static KzBookmark *
rast_create_search_result_bookmark (gint out, const gchar *text)
{
	GIOChannel *io;
	gchar *line;
	gsize length;
	gchar *title = NULL, *uri = NULL, *desc = NULL;
	KzBookmark *result;

	io = g_io_channel_unix_new(out);
	g_io_channel_set_encoding(io, NULL, NULL);

	result = kz_bookmark_pure_folder_new();
	
	while (g_io_channel_read_line(io, &line, &length, NULL, NULL) == G_IO_STATUS_NORMAL)
	{
		if (g_str_has_prefix(line, "</document>"))
		{
			KzBookmark *child;
			child = kz_bookmark_new_with_attrs(title, uri, desc);
			kz_bookmark_append(result, child);
			g_object_unref(child);
			g_free(desc);
			g_free(title);
			g_free(uri);
		}
		else if (g_str_has_prefix(line, "<uri>"))
		{
			gchar *dirname, *orig_uri;
			gchar *link;
			size_t len;
			link = xml_get_attr(line, "uri");
			dirname = g_strconcat(g_get_home_dir(), 
					      HISTORY_DIR,
					      NULL);
			len = strlen(dirname);
			orig_uri = create_uri_from_filename(link + strlen("file://") + len);
			uri = url_decode(orig_uri);
			g_free(orig_uri);			
			g_free(dirname);
			g_free(link);
		}
		else if (g_str_has_prefix(line, "<title>"))
		{
			title = xml_get_content(line);
		}
		else if (g_str_has_prefix(line, "<summary"))
		{
			gchar *summary = xml_get_content(line);
			desc = remove_tag(summary, g_strlen(summary));
			g_free(summary);
		}
		g_free(line);
	}
	g_io_channel_unref(io);

	return result;
}

KzBookmark *
rast_get_search_result_bookmark (const gchar *text)
{
	gint out;

	if (!text) return NULL;
	if (!exists_search_cmd) return NULL;

	if (!rast_execute_search_command(text, &out))
		return NULL;

	return rast_create_search_result_bookmark(out, text);
}

void
rast_make_index(void)
{
	const gchar *rast_create = "rast create --preserve-text --property=title:string:search "; 
	gchar *command;
	gint argc;
	gchar **argv = NULL;
	GSpawnFlags flags;
	GPid pid;
	
	command = g_strconcat(rast_create, 
			      g_get_home_dir(),
			      HISTORY_INDEX" ",
			      g_get_home_dir(),
			      HISTORY_DIR,
			      NULL);

	g_shell_parse_argv(command,
			   &argc,
			   &argv,
			   NULL);
	flags = G_SPAWN_SEARCH_PATH |
		G_SPAWN_STDOUT_TO_DEV_NULL;
	
	g_spawn_async(NULL,
		      argv,
		      NULL,
		      flags,
		      NULL,
		      NULL,
		      &pid,
		      NULL);
	g_strfreev(argv);
	g_free(command);
}

gboolean
rast_exist_index_dir(void)
{
	gchar *index_dir;
	gboolean exist = FALSE;

	index_dir = g_build_filename(g_get_home_dir(),
			HISTORY_INDEX, NULL);
	exist = g_file_test(index_dir, G_FILE_TEST_IS_DIR);
	g_free(index_dir);

	return exist;
}

gchar *get_value (const gchar *line)
{
	gchar *p;

	p = strchr(line, ':');
	p += 2;

	return g_strchomp(g_strdup(p));
}
