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

/*
 *  Copyright (C) 2007 Kouhei Sutou <kou@cozmixng.org>
 *  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.
 */

/* NOTE:temporary fix for declaration strcasestr */
#define _GNU_SOURCE
#include <string.h>

#include <ctype.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <estraier.h>
#include <estmtdb.h>

#include "kazehakase.h"
#include "utils/utils.h"
#include "kz-search.h"
#include "kz-search-common.h"
#include "kz-module-impl.h"
#include "egg-pixbuf-thumbnail.h"

#define HISTORY_INDEX_SUFFIX "_index.hest"

#define KZ_TYPE_HYPER_ESTRAIER_SEARCH		(kz_type_hyper_estraier_search)
#define KZ_HYPER_ESTRAIER_SEARCH(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), KZ_TYPE_HYPER_ESTRAIER_SEARCH, KzHyperEstraierSearch))
#define KZ_IS_HYPER_ESTRAIER_SEARCH(obj)	(G_TYPE_CHECK_INSTANCE_TYPE ((obj), KZ_TYPE_HYPER_ESTRAIER_SEARCH))
#define KZ_HYPER_ESTRAIER_SEARCH_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), KZ_TYPE_HYPER_ESTRAIER_SEARCH, KzHyperEstraierSearchClass))
#define KZ_IS_HYPER_ESTRAIER_SEARCH_CLASS(klass) 	(G_TYPE_CHECK_CLASS_TYPE ((klass), KZ_TYPE_HYPER_ESTRAIER_SEARCH))
#define KZ_HYPER_ESTRAIER_SEARCH_GET_CLASS(obj)  	(G_TYPE_INSTANCE_GET_CLASS ((obj), KZ_TYPE_HYPER_ESTRAIER_SEARCH, KzHyperEstraierSearchClass))

typedef struct _KzHyperEstraierSearch		KzHyperEstraierSearch;

struct _KzHyperEstraierSearch
{
	GObject parent;

	gchar *dbname;
	gchar *cache_path;
	size_t cache_path_len;

	ESTMTDB *db;
};

typedef struct _KzHyperEstraierSearchClass	KzHyperEstraierSearchClass;
struct _KzHyperEstraierSearchClass
{
	GObjectClass parent_class;
};


/* KzHyperEstraierSearch Class */
static void   kz_hyper_estraier_search_class_init (KzHyperEstraierSearchClass *klass);
static void   kz_hyper_estraier_search_init       (KzHyperEstraierSearch *search);

/* GObject Class */
static void     dispose      (GObject *object);

/* KzSearchIFace */
static void        kz_hyper_estraier_search_iface_init      (KzSearchIFace *iface);


static gchar	  *get_search_result_html     (KzSearch *search, const gchar *text);
static KzBookmark *get_search_result_bookmark (KzSearch *search, const gchar *text);
static gboolean    register_document          (KzSearch *search,
					       const gchar *uri,
					       const gchar *title,
					       const gchar *contents,
					       GTime mtime);
static gboolean    unregister_document        (KzSearch *search, const gchar *uri);
static GPid        optimize_index             (KzSearch *search);
static void        make_index                 (KzSearch *search);
static gboolean    exist_index_dir            (KzSearch *search);


static GObjectClass *parent_class;
static GType kz_type_hyper_estraier_search = 0;

static void
kz_hyper_estraier_search_register_type (GTypeModule *module)
{
	static const GTypeInfo kz_hyper_estraier_search_info =
	{
		sizeof (KzHyperEstraierSearchClass),
		NULL,		/* base_init */
		NULL,		/* base_finalize */
		(GClassInitFunc) kz_hyper_estraier_search_class_init,
		NULL,		/* class_finalize */
		NULL,		/* class_data */
		sizeof (KzHyperEstraierSearch),
		0,		/* n_preallocs */
		(GInstanceInitFunc) kz_hyper_estraier_search_init,
	};

	const GInterfaceInfo kz_search_info =
	{
		(GInterfaceInitFunc) kz_hyper_estraier_search_iface_init,
		NULL,
		NULL
	};

	kz_type_hyper_estraier_search =
		g_type_module_register_type (module,
					     G_TYPE_OBJECT,
					     "KzHyperEstraierSearch",
					     &kz_hyper_estraier_search_info, 0);

	g_type_module_add_interface(module,
				    KZ_TYPE_HYPER_ESTRAIER_SEARCH,
				    KZ_TYPE_SEARCH,
				    &kz_search_info);
}

G_MODULE_EXPORT void
KZ_MODULE_IMPL_INIT (GTypeModule *module)
{
	kz_hyper_estraier_search_register_type(module);
}

G_MODULE_EXPORT void
KZ_MODULE_IMPL_EXIT (void)
{
}

G_MODULE_EXPORT GObject *
KZ_MODULE_IMPL_INSTANTIATE (const gchar *first_property,
			    va_list      var_args)
{
	return g_object_new_valist(KZ_TYPE_HYPER_ESTRAIER_SEARCH,
				   first_property, var_args);
}

G_MODULE_EXPORT const gchar *
KZ_MODULE_IMPL_GET_NAME (void)
{
	return "Hyper Estraier " _EST_VERSION;
}


static void
kz_hyper_estraier_search_class_init (KzHyperEstraierSearchClass *klass)
{
	GObjectClass *object_class;

	parent_class = g_type_class_peek_parent (klass);
	object_class = G_OBJECT_CLASS(klass);

	object_class->dispose     = dispose;
}

static void
kz_hyper_estraier_search_iface_init (KzSearchIFace *iface)
{
	iface->get_search_result_html     = get_search_result_html;
	iface->get_search_result_bookmark = get_search_result_bookmark;
	iface->register_document          = register_document;
	iface->unregister_document        = unregister_document;
	iface->optimize_index             = optimize_index;
	iface->make_index                 = make_index;
	iface->exist_index_dir            = exist_index_dir;
}

static void
close_db (KzHyperEstraierSearch *search)
{
	g_return_if_fail(KZ_IS_HYPER_ESTRAIER_SEARCH(search));

	if (search->db)
	{
		int ecode;
		if (!est_mtdb_close(search->db, &ecode))
			g_warning("db close error: %s", est_err_msg(ecode));
	}

	search->db = NULL;
}

static void
open_db (KzHyperEstraierSearch *search)
{
	int ecode;

	g_return_if_fail(KZ_IS_HYPER_ESTRAIER_SEARCH(search));
	g_return_if_fail(search->dbname);

	if (search->db)
		close_db(search);

	search->db = est_mtdb_open(search->dbname,
				   ESTDBWRITER | ESTDBCREAT | ESTDBNOLCK,
				   &ecode);
	if (search->db)
		return;

	g_warning("db open error (try repairing): %s", est_err_msg(ecode));
	if (!est_db_repair(search->dbname, ESTRPSTRICT, &ecode))
	{
		g_warning("db repair error: %s", est_err_msg(ecode));
		return;
	}

	search->db = est_mtdb_open(search->dbname, ESTDBWRITER, &ecode);
	if (!search->db)
		g_warning("db open error: %s", est_err_msg(ecode));
}

static void
ensure_open_db (KzHyperEstraierSearch *search)
{
	g_return_if_fail(KZ_IS_HYPER_ESTRAIER_SEARCH(search));

	if (!search->db)
		open_db(search);
}

static void
kz_hyper_estraier_search_init (KzHyperEstraierSearch *search)
{
	search->cache_path = g_strdup(KZ_GET_HISTORY_DIR);
	search->dbname = g_strconcat(search->cache_path, HISTORY_INDEX_SUFFIX,
				     NULL);

	search->cache_path_len = strlen(search->cache_path);
}

static void
dispose (GObject *object)
{
	KzHyperEstraierSearch *search;

	search = KZ_HYPER_ESTRAIER_SEARCH(object);

	close_db(search);

	if (search->dbname)
		g_free(search->dbname);
	if (search->cache_path)
		g_free(search->cache_path);

	search->dbname = NULL;
	search->cache_path = NULL;

	if (G_OBJECT_CLASS(parent_class)->dispose)
		G_OBJECT_CLASS(parent_class)->dispose(object);
}


static gchar *
create_search_result_html (KzSearch *search, const gchar *text)
{
	ESTCOND *cond;
	CBLIST *highlights;
	int *results, n_results, i;
	gchar *except_word, *tmp, *utf8;
	gchar **texts;
	GString *html, *phrase, *attr_uri_phrase, *desc_str;
	gint num_summary = 128, max_results = 20, half_of_summary;
	KzHyperEstraierSearch *he_search;

	g_return_val_if_fail(KZ_IS_HYPER_ESTRAIER_SEARCH(search), NULL);
	he_search = KZ_HYPER_ESTRAIER_SEARCH(search);

	ensure_open_db(he_search);
	g_return_val_if_fail(he_search->db, NULL);

	cond = est_cond_new();
	highlights = cblistopen();
	
	/* convert from kz_conf except keyword */
	phrase = g_string_new(text);
	except_word = KZ_CONF_GET_STR("History", "except_keyword");
	if (except_word && *except_word)
	{
		texts = g_strsplit(except_word, ",", -1);
		g_free(except_word);
		tmp = g_strjoinv(" -", texts);
		g_strfreev(texts);
		g_string_append(phrase, " -");
		g_string_append(phrase, tmp);
		g_free(tmp);
	}
	tmp = g_utf8_normalize(phrase->str,-1, G_NORMALIZE_ALL_COMPOSE);
	g_string_free(phrase, TRUE);
	utf8 = g_utf8_strdown(tmp, -1);
	g_free(tmp);
	
	/* start word split */
	texts = g_strsplit(utf8, " ", -1);
	g_free(utf8);
	phrase = g_string_sized_new(0);

	for (i = 0; texts[i]; i++)
	{
		tmp = texts[i];
		guint flag = 0;
		gint cond_i;
		if(!tmp || !*tmp || g_unichar_isspace(*tmp)) continue;
		/* extract supported condition [ALL KZ_HISTORY_SEARCH CODE] */
		for(cond_i = 0; cond_i < KZ_SEARCH_FLAG_SIZE; cond_i++)
		{
			if(g_str_has_prefix(tmp, KZ_SEARCH_FLAGS[cond_i].exp))
			{
				flag |= KZ_SEARCH_FLAGS[cond_i].mask;
				tmp += strlen(KZ_SEARCH_FLAGS[cond_i].exp);
			}
		}
		/* write out for search engine expression [ENGINE SPECIFIC CODE] */
		switch(flag & KZ_SEARCH_FLAG_GROUP_OPTION) {
		case KZ_SEARCH_FLAG_MASK_SITE:
			attr_uri_phrase = g_string_sized_new(0);
			g_string_printf(attr_uri_phrase,
					"%s %s %s",
					ESTDATTRURI,
					(flag & KZ_SEARCH_FLAG_MASK_NOT)?"!ISTRINC":"ISTRINC",
					tmp);
			est_cond_add_attr(cond, attr_uri_phrase->str);
			g_string_free(attr_uri_phrase, TRUE);
			break;
		default:
			if(flag & KZ_SEARCH_FLAG_MASK_NOT)
			{
				g_string_append(phrase, " " ESTOPDIFF " ");
			}
			else
			{
				g_string_append(phrase, " " ESTOPISECT " ");
				cblistpush(highlights, tmp, -1);
			}
			g_string_append(phrase, tmp);
		}
	}
	g_strfreev(texts);

	if(strlen(phrase->str) > 0) { 
		est_cond_set_phrase(cond, phrase->str);
	}
	g_string_free(phrase, TRUE);

	/* start actual search */
	KZ_CONF_GET("History", "num_summary", num_summary, INT);
	KZ_CONF_GET("History", "max_results", max_results, INT);
	half_of_summary = num_summary / 2;

	results = est_mtdb_search(he_search->db, cond, &n_results, NULL);

	/* almost in typical num_summary*2 */
	desc_str = g_string_sized_new(num_summary*2 - 1);
	/* in typical (num_summary*2+html_tags)*max_results */
	html = g_string_sized_new((num_summary*2+512)*max_results);

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

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

	for (i = 0; i < MIN(n_results, max_results); i++)
	{
		ESTDOC *doc;
		const gchar *title, *date, *cache_link;
		gchar *cache_str, *thumb_filename, *uri, *thumb_uri;
		gchar *desc, *p, *l;

		doc = est_mtdb_get_doc(he_search->db, results[i], 0);
		if (!doc)
			continue;

		cache_link = est_doc_attr(doc, ESTDATTRURI);
		if (g_file_test(cache_link+strlen("file://"), G_FILE_TEST_EXISTS))
		{
			gchar *escaped_cache_link;
			escaped_cache_link = kz_uri_encode_last_component(cache_link);
			cache_str = g_strdup_printf("<span class=\"cache\"><a href=\"%s\">cache</a>"
			                            "</span>\n", escaped_cache_link);
			g_free(escaped_cache_link);
		}
		else
		{
			cache_str = g_strdup("");
		}
		uri = create_uri_from_filename(cache_link +
					       strlen("file://") +
					       he_search->cache_path_len +
					       strlen(G_DIR_SEPARATOR_S));

		thumb_filename =
			egg_pixbuf_get_thumb_filename(uri,
						      EGG_PIXBUF_THUMBNAIL_LARGE);
		thumb_uri = g_strdup_printf("history-search:?image=%s",
					    thumb_filename);
		g_free(thumb_filename);

		title = est_doc_attr(doc, ESTDATTRTITLE);
		if(!title || !*title) title = "[NO TITLE]";
		date = est_doc_attr(doc, ESTDATTRMDATE);

		desc = est_doc_make_snippet(doc, highlights, num_summary,
					    half_of_summary, half_of_summary);
		/* make highlight keyword */
		desc_str->len = 0;	/* reset contain length */
		l = p = desc;
		while (*p)
		{
			if (*p == '\n')
			{
				g_string_append_len(desc_str, l, p - l);
				l = ++p;
				continue;
			}

			if (*p == '\t')
			{
				g_string_append(desc_str, "<b>");
				g_string_append_len(desc_str, l, p - l);
				g_string_append(desc_str, "</b>");
				while (*p)
				{
					if (*p == '\n')
					{
						l = ++p;
						break;
					}
					p++;
				}
				if (*p == '\0')
				{
					l = NULL;
					break;
				}
				continue;
			}
			p++;
		}
		if (p != l && l)
			g_string_append_len(desc_str, l, p - l);
		free(desc);		/* don't g_free because born from estraier func */

		g_string_append_printf(html,
				       CONTENT,
				       uri,
				       title,
				       thumb_uri, /* thumbnail */
				       desc_str->str,
				       uri,
				       cache_str,
				       date);
		g_free(uri);
		g_free(cache_str);
		g_free(thumb_uri);
		est_doc_delete(doc);
	}
	free(results);
	g_string_free(desc_str, TRUE);

	cblistclose(highlights);
	est_cond_delete(cond);

	g_string_append_printf(html, FOOTER, _EST_PROJURL, "Hyper Estraier", _EST_VERSION);
	g_string_append(html, "</body></html>");

	return g_string_free(html, FALSE);
}

gchar *
get_search_result_html (KzSearch *search, const gchar *text)
{
	if (!text) return NULL;

	return create_search_result_html(search, text);
}

static gchar *
get_document_encoding (const gchar *contents)
{
	gchar *encoding = NULL;
	gchar *p;

	if (!contents) return NULL;

	p = (gchar*)contents;
	while ((p = strstr(p, "<meta ")))
	{
		gchar *end, *quote_end;
		p = strstr(p, "http-equiv=\"");
		if (!p) break;

		p+=12;
		end = strchr(p, '"');
		if (!end) break;

		if (g_ascii_strncasecmp(p, "content-type", end - p))
			continue;

		p = end;
		/* negligent */
		p = strstr(p, "charset=");
		if (!p) break;
		p+=8;
		end = strchr(p, ';');
		quote_end = strchr(p, '"');
		if(quote_end && quote_end < end) end = quote_end;
		if (!end) break;

		encoding = g_strndup(p, end - p);
		break;
	}

	return encoding;
}


/* Type and functions that used in gMmarkupParser */
#if 0 /* drop at the tiem */
typedef struct {
	gint in_title;
	gchar *title;
} title_parsing;
static void title_start_element(GMarkupParseContext *context, const gchar *element_name,
			 const gchar **attribute_names, const gchar **attribute_values,
			 gpointer user_data, GError **error)
{
	gchar *tag = g_utf8_strup(element_name, -1);
//	printf("what elem:%s\n", tag);
	if(g_strcmp(tag, "TITLE") == 0) {
	    ((title_parsing *) user_data)->in_title = 1;
	} else if(g_strcmp(tag, "BODY") == 0) {
	    g_markup_parse_context_end_parse(context, error);
	}
	g_free(tag);
}
static void title_end_element(GMarkupParseContext *context, const gchar         *element_name,
                       gpointer             user_data, GError             **error)
{
	gchar *tag = g_utf8_strup(element_name, -1);
	if(g_strcmp(tag, "META") == 0) {
		g_markup_parse_context_end_parse(context, error);
	}
	g_free(tag);
}
static void title_text(GMarkupParseContext *context, const gchar *text,
                gsize text_len, gpointer user_data, GError **error)
{
	if(((title_parsing *) user_data)->in_title != 0) {
		((title_parsing *) user_data)->title = g_strndup(text, text_len);
		printf("title get in func:%s\n", ((title_parsing *) user_data)->title);
		g_markup_parse_context_end_parse(context, error);
		((title_parsing *) user_data)->in_title = 0;
	}
}
#endif
/**
 * create page title from html content string
 * currentlly called from only register_documents_in_path().
 * @param contents utf-8 strings that contain html contents
 * @return gchar* new allocated, should be g_free or NULL for failed parsing
 */
static gchar *
get_document_title(const gchar *contents)
{
#if 0
	/* didn't work yet. */
	GMarkupParser title_parser_funcs = {
		title_start_element, title_end_element,
		title_text, NULL, NULL
	};
	title_parsing data = {0, NULL};
	GMarkupParseContext *title_parser;
	
	title_parser = g_markup_parse_context_new(&title_parser_funcs,
						  0,
						  &data,
						  NULL);
	if(!title_parser) {
	    printf("parser create fail.\n");
	    return NULL;
	}
	g_markup_parse_context_parse(title_parser, contents, 1024, NULL);
	g_markup_parse_context_free(title_parser);
	printf("get title:%s\n", data.title);
	return data.title;
#endif 
	/* simple parser */
	//FIXME: parse correctly
	gchar *start;
	start = strcasestr(contents, "<title>");
	if (start == NULL)
	{
	    //printf("not in title.\n");
	    return NULL;
	}
	start += strlen("<title>");
	return g_strndup(start, strstr(start, "<") - start);
}

/**
 * get utf-8 contents string for given html file
 * @param filepath file name to get document contents.
 * @return gchar* newly allocated utf-8 string, or NULL failed.
 */
static gchar *
get_utf8_contents(const char *filepath)
{ 
	gchar *contents = NULL;

	if (g_file_get_contents(filepath, &contents, NULL, NULL))
	{
		gchar *encoding = get_document_encoding(contents);
		if (!encoding)
			encoding = g_strdup(est_enc_name(contents,
							 strlen(contents),
							 ESTLANGJA));

		if (g_ascii_strcasecmp(encoding, "UTF-8") != 0)
		{
			gchar *utf8_contents, *upper_encoding;
			upper_encoding = g_ascii_strup(encoding, -1);
			utf8_contents = g_convert(contents, -1,
						  "UTF-8", upper_encoding,
						  NULL, NULL,
						  NULL);
			g_free(upper_encoding);
			g_free(contents);
			if (utf8_contents)
			{
				contents = utf8_contents;
			}
			else
			{
				g_warning("failed convert encoding. [enc:file] [%s:%s]\n",
					  encoding, filepath);
				contents = NULL;
			}
		}
		g_free(encoding);
	}

	return contents;
}

gboolean
register_document (KzSearch *search, const gchar *uri, const gchar *title, const gchar *contents, GTime mtime)
{
	ESTDOC *doc;
	gboolean success;
	gchar *time_str, *filename, *text = NULL;
	gchar *tmp_filename, *tmp_path;
	KzHyperEstraierSearch *he_search;

	g_return_val_if_fail (uri, FALSE);

	g_return_val_if_fail(KZ_IS_HYPER_ESTRAIER_SEARCH(search), FALSE);
	he_search = KZ_HYPER_ESTRAIER_SEARCH(search);

	ensure_open_db(he_search);
	g_return_val_if_fail(he_search->db, FALSE);

	doc = est_doc_new();

	tmp_filename = create_filename_with_path_from_uri(uri);
	tmp_path = g_build_filename(he_search->cache_path, tmp_filename, NULL);
	filename = g_strdup_printf("file://%s", tmp_path);
	g_free(tmp_path);
	g_free(tmp_filename);
	est_doc_add_attr(doc, ESTDATTRURI, filename);
	g_free(filename);

	if (title)
		est_doc_add_attr(doc, ESTDATTRTITLE, title);

	time_str = cbdatestrwww(mtime, 0);
	est_doc_add_attr(doc, ESTDATTRMDATE, time_str);
	g_free(time_str);

	text = html_to_text(contents);
	if (text)
	{
		est_doc_add_text(doc, text);
		g_free(text);
	}

	success = est_mtdb_put_doc(he_search->db, doc, ESTPDCLEAN) != 0;
	if (!success)
	{
		g_warning("register error: %s", est_err_msg(est_mtdb_error(he_search->db)));
		g_warning("retry...");
		est_mtdb_sync(he_search->db);
		if((success = est_mtdb_put_doc(he_search->db, doc, 0)))
		{
		    g_warning("succeed!");
		}
		else
		{
		    g_warning("register error: %s", est_err_msg(est_mtdb_error(he_search->db)));
		    g_warning("drop %s,%s\n", ESTDATTRURI, est_doc_attr(doc, ESTDATTRURI));
		}
	}

	est_doc_delete(doc);

	return success;
}

gboolean
unregister_document (KzSearch *search, const gchar *uri)
{
	int id;
	gboolean success;
	KzHyperEstraierSearch *he_search;

	g_return_val_if_fail(KZ_IS_HYPER_ESTRAIER_SEARCH(search), FALSE);
	he_search = KZ_HYPER_ESTRAIER_SEARCH(search);

	ensure_open_db(he_search);
	g_return_val_if_fail(he_search->db, FALSE);

	id = est_mtdb_uri_to_id(he_search->db, uri);
	if (id == -1)
		success = FALSE;
	else
		success = est_mtdb_out_doc(he_search->db, id, ESTODCLEAN) != 0;

	return success;
}

static GPid
optimize_index (KzSearch *search)
{
	KzHyperEstraierSearch *he_search;

	g_return_val_if_fail(KZ_IS_HYPER_ESTRAIER_SEARCH(search), 0);
	he_search = KZ_HYPER_ESTRAIER_SEARCH(search);

	ensure_open_db(he_search);
	g_return_val_if_fail(he_search->db, 0);

	est_mtdb_optimize(he_search->db, 0);

#warning FIXME! return GSource or something.
	return 0;
}

static KzBookmark *
get_search_result_bookmark (KzSearch *search, const gchar *text)
{
	/* not implemented yet */
	return NULL;
}

/**
 * register all documents in given path
 * @param search this search class
 * @param path path to the directory contains contents to register
 * @return void
 */
//TODO:correctly parse title (get_document_title())
static void
register_documents_in_path (KzSearch *search, const gchar *path)
{
	GDir *gd;
	const gchar *file;
	KzHyperEstraierSearch *he_search;

	gd = g_dir_open(path, 0, NULL);
	if (!gd)
		return;

	he_search = KZ_HYPER_ESTRAIER_SEARCH(search);
	while ((file = g_dir_read_name (gd)))
	{
		gchar *new_path = g_build_filename (path, file, NULL);
		if (g_file_test(new_path, G_FILE_TEST_IS_DIR))
		{
			register_documents_in_path(search, new_path);
		}
		else
		{
			gchar *uri, *title, *contents;
			GTime mtime;
			struct stat st;

			/* filter for time-stamp */
			//TODO:history search result, optional
			/* NOTE:string pointer compare is designed */
			if (path == he_search->cache_path && strcmp(file, "time-stamp") == 0)
				goto  END_NEW_PATH;

			contents = get_utf8_contents(new_path);
			if (!contents)
				goto END_NEW_PATH;

			g_stat(new_path, &st);
			mtime = st.st_mtime;
			title = get_document_title(contents);
			uri = create_uri_from_filename(new_path +
						       he_search->cache_path_len +
						       1);
			register_document(search, uri, title, contents, mtime);

			if (title)
				g_free(title);
			g_free(uri);
			g_free(contents);
		}
		END_NEW_PATH:
		g_free(new_path);
	}
	g_dir_close (gd);
}

static void
make_index (KzSearch *search)
{
	KzHyperEstraierSearch *he_search;

	g_return_if_fail(KZ_IS_HYPER_ESTRAIER_SEARCH(search));
	he_search = KZ_HYPER_ESTRAIER_SEARCH(search);

	ensure_open_db(he_search);
	g_return_if_fail(he_search->db);

	register_documents_in_path(search, he_search->cache_path);
}

static gboolean
exist_index_dir(KzSearch *search)
{
	KzHyperEstraierSearch *he_search;

	g_return_val_if_fail(KZ_IS_HYPER_ESTRAIER_SEARCH(search), FALSE);
	he_search = KZ_HYPER_ESTRAIER_SEARCH(search);

	return g_file_test(he_search->dbname, G_FILE_TEST_IS_DIR);
}
