/*

Namazu.xs

# Copyright (C) 1999-2006 NOKUBI Takatsugu All rights reserved.
# This is free software with ABSOLUTELY NO WARRANTY.
# 
# 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 of the License, 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

$Id: Namazu.xs 268 2006-06-09 05:48:57Z knok $

*/

#ifdef __cplusplus
extern "C" {
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include <stdio.h>
#include <namazu/libnamazu.h>
#include <namazu/codeconv.h>
#include <namazu/field.h>
#include <namazu/hlist.h>
#include <namazu/idxname.h>
#include <namazu/parser.h>
#include <namazu/re.h>
#include <namazu/search.h>
#include <namazu/util.h>
#include <namazu/wakati.h>
#ifdef __cplusplus
}
#endif

/* for old perl (< 5.004_04?) */
#if !defined(PL_na) && defined(na)
#define PL_na na
#endif
#if !defined(PL_sv_undef) && defined(sv_undef)
#define PL_sv_undef sv_undef
#endif
#if !defined(SvPV_nolen) /* for perl 5.005 */
#define SvPV_nolen(x) SvPV(x, PL_na)
#endif

#define SEARCH_NAMAZU_FIELDS 5
#define SCORE "//score"
#define SCORE_LEN strlen(SCORE)
#define DATE "//date"
#define DATE_LEN strlen(DATE)
#define RANK "//rank"
#define RANK_LEN strlen(RANK)
#define DOCID "//docid"
#define DOCID_LEN strlen(DOCID)
#define IDXID "//idxid"
#define IDXID_LEN strlen(IDXID)

static int status = 0;

AV * call_search_main_c(char *query, int maxget)
{
	AV *retar;
	NmzResult hlist;
	char result[SEARCH_NAMAZU_FIELDS][BUFSIZE];
	int i;
	status = 0;
	retar = newAV();
	hlist = nmz_search(query);
	av_extend(retar, hlist.num - 1);
	status = hlist.stat;
	for (i = 0; i < hlist.num; i ++) {
	    if (i < maxget) {
		SV *ohlist = perl_eval_pv("new Search::Namazu::Result", TRUE);
		dSP;
		nmz_get_field_data(hlist.data[i].idxid, hlist.data[i].docid, "uri", result[0]);
		nmz_get_field_data(hlist.data[i].idxid, hlist.data[i].docid, "summary", result[1]);
		nmz_get_field_data(hlist.data[i].idxid, hlist.data[i].docid, "title", result[2]);
		nmz_get_field_data(hlist.data[i].idxid, hlist.data[i].docid, "author", result[3]);
		nmz_get_field_data(hlist.data[i].idxid, hlist.data[i].docid, "size", result[4]);
		ENTER;
		SAVETMPS;
		PUSHMARK(SP);
		PUSHs(ohlist);
		PUSHs(sv_2mortal(newSViv(hlist.data[i].score)));
		PUSHs(sv_2mortal(newSVpv(result[0], strlen(result[0]))));
		PUSHs(sv_2mortal(newSViv(hlist.data[i].date)));
		PUSHs(sv_2mortal(newSViv(hlist.data[i].rank)));
		PUSHs(sv_2mortal(newSVpv(result[1], strlen(result[1]))));
		PUSHs(sv_2mortal(newSVpv(result[2], strlen(result[2]))));
		PUSHs(sv_2mortal(newSVpv(result[3], strlen(result[3]))));
		PUSHs(sv_2mortal(newSVpv(result[4], strlen(result[4]))));
		PUTBACK;
		perl_call_method("set", G_DISCARD);
		SvREFCNT_inc(ohlist);
		av_store(retar, i, ohlist);
		FREETMPS;
		LEAVE;
	    } else {
		av_store(retar, i, &PL_sv_undef);
	    }	   
	}
	nmz_free_hlist(hlist);
	return retar;
}

AV * call_search_main_fields_c(char *query, int maxget, AV *fields)
{
	AV *retar;
	HV *stash;
	char **fstr;
	int *fsize;
	NmzResult hlist;
	int i;
	int flen;

	retar = newAV();
	flen = av_len(fields);
	if (flen < 0 || maxget <= 0 || query == NULL)
		return retar;
	status = 0;
	fstr = (char **) malloc(sizeof(char *) * (flen + 1));
	fsize = (int *) malloc(sizeof(int) * (flen + 1));
	for (i = 0; i <= flen; i ++) {
		SV **x;
		x = av_fetch(fields, i, 0);
		fstr[i] = SvPV_nolen(*x);
		fsize[i] = SvCUR(*x);
	}
	hlist = nmz_search(query);
	av_extend(retar, hlist.num - 1);
	status = hlist.stat;
	stash = gv_stashpv("Search::Namazu::ResultXS", 0);
	for (i = 0; i < hlist.num; i ++) {
		if (i < maxget) {
			HV *hash;
			SV *ref;
			SV *score, *date, *rank, *docid, *idxid;
			int j;
			char fcont[BUFSIZE];

			hash = newHV();
			for (j = 0; j <= flen; j ++) {
				nmz_get_field_data(hlist.data[i].idxid,
					hlist.data[i].docid,
					fstr[j], fcont);
				hv_store(hash, fstr[j], fsize[j], 
					newSVpv(fcont, strlen(fcont)), 0);
			}
			score = newSViv(hlist.data[i].score);
			date = newSViv(hlist.data[i].date);
			rank = newSViv(hlist.data[i].rank);
			docid = newSViv(hlist.data[i].docid);
			idxid = newSViv(hlist.data[i].idxid);
			hv_store(hash, SCORE, SCORE_LEN, score, 0);
			hv_store(hash, DATE, DATE_LEN, date, 0);
			hv_store(hash, RANK, RANK_LEN, rank, 0);
			hv_store(hash, DOCID, IDXID_LEN, docid, 0);
			hv_store(hash, IDXID, IDXID_LEN, idxid, 0);

			ref = newRV_inc((SV*) hash);
			sv_bless(ref, stash);
			av_store(retar, i, ref);
		} else {
			av_store(retar, i, &PL_sv_undef);
		}
	}
	nmz_free_hlist(hlist);
	free(fstr);
	free(fsize);
	return retar;
}

MODULE = Search::Namazu		PACKAGE = Search::Namazu

PROTOTYPES: DISABLE

void
call_search_main(query, maxget)
	SV *query
	int maxget

	PPCODE:
		char *qstr;
                char buffer[BUFSIZE];
		char cqstr[BUFSIZE * 2];
		AV *retar;
		int i;

		qstr = SvPV(query, PL_na);
                strncpy(buffer, qstr, BUFSIZE);
                buffer[BUFSIZE - 1] = '\0';
		nmz_codeconv_query(buffer);
		strcpy(cqstr, buffer);
		retar = call_search_main_c(cqstr, maxget);
#if ! defined(PERL_VERSION) || (PERL_VERSION == 6 && PERL_SUBVERSION == 0)
		{ /* workaround for only one result */
                        SPAGAIN;
		}
#endif /* PERL_VERSION */
		while (av_len(retar) >= 0) {
			XPUSHs(av_shift(retar));
		}
		nmz_free_internal();

SV*
call_search_main_ref(query, maxget)
	SV *query
	int maxget

	CODE:
		char *qstr;
                char buffer[BUFSIZE];
		char cqstr[BUFSIZE * 2];
		AV *retar;
		int i;

		qstr = SvPV(query, PL_na);
                strncpy(buffer, qstr, BUFSIZE);
                buffer[BUFSIZE - 1] = '\0';
                nmz_codeconv_query(buffer);
                strcpy(cqstr, buffer);
		retar = call_search_main_c(cqstr, maxget);
		nmz_free_internal();
		RETVAL = newRV_inc((SV*) retar);
	OUTPUT:
		RETVAL

SV*
call_search_main_fields(query, maxget, fieldref)
	SV *query
	int maxget
	SV *fieldref

	CODE:
		char *qstr;
                char buffer[BUFSIZE];
		char cqstr[BUFSIZE * 2];
		AV *retar;
		AV *fields;
		int i;

		fields = (AV *) SvRV(fieldref);
		qstr = SvPV(query, PL_na);
                strncpy(buffer, qstr, BUFSIZE);
                buffer[BUFSIZE - 1] = '\0';
                nmz_codeconv_query(buffer);
                strcpy(cqstr, buffer);
		retar = call_search_main_fields_c(cqstr, maxget, fields);
		nmz_free_internal();
		RETVAL = newRV_inc((SV*) retar);
	OUTPUT:
		RETVAL

int
nmz_addindex(index)
	SV *index

	PREINIT:
		char *tmp;

	CODE:
		tmp = SvPV(index, PL_na);
		RETVAL = nmz_add_index(tmp);

	OUTPUT:
		RETVAL

void
nmz_sortbydate()
	CODE:
		nmz_set_sortmethod(SORT_BY_DATE);

void
nmz_sortbyscore()
	CODE:
		nmz_set_sortmethod(SORT_BY_SCORE);

void
nmz_setsortfield(field)
	SV * field
	CODE:
		nmz_set_sortfield(SvPV_nolen(field));

void
nmz_sortbyfield()
	CODE:
		nmz_set_sortmethod(SORT_BY_FIELD);

void
nmz_descendingsort()
	CODE:
		nmz_set_sortorder(DESCENDING);

void
nmz_ascendingsort()
	CODE:
		nmz_set_sortorder(ASCENDING);

int
nmz_setlang(lang)
	SV *lang

	PREINIT:
		char *tmp;

	CODE:
		tmp = SvPV(lang, PL_na);
		RETVAL = nmz_set_lang(tmp);

	OUTPUT:
		RETVAL

void
nmz_setmaxhit(max)
	int max

	CODE:
		nmz_set_maxhit(max);

int
nmz_getstatus()
	CODE:
		RETVAL = status;
	OUTPUT:
		RETVAL


MODULE = Search::Namazu	PACKAGE = Search::Namazu::ResultXS	PREFIX = res_

SV *
res_new()
	CODE:
		HV *self;
		HV *stash;
		SV *ref;

		stash = gv_stashpv("Search::Namazu::ResultXS", 0);
		self = newHV();
		ref = newRV_inc((SV*) self);
		sv_bless(ref, stash);
		RETVAL = ref;
	OUTPUT:
		RETVAL

void
res_set(self, key, val)
	      SV *self
	      SV *key
	      SV *val
	CODE:
		HV *hash;

		hash = (HV *) SvRV(self);
		hv_store(hash, SvPV_nolen(key), SvCUR(key), val, 0);

SV *
res_get(self, key)
	      SV *self
	      SV *key
	CODE:
		HV *hash;
		SV **ret;

		hash = (HV *) SvRV(self);
		ret = hv_fetch(hash, SvPV_nolen(key), SvCUR(key), 0);
		if (ret == NULL) {
			RETVAL = &PL_sv_undef;
		} else {
			RETVAL = SvREFCNT_inc(*ret);
		}
	OUTPUT:
		RETVAL

SV *
res_score(self)
	      SV *self
	CODE:
		HV *hash;
		SV **ret;

		hash = (HV *) SvRV(self);
		ret = hv_fetch(hash, SCORE, SCORE_LEN, 0);
		if (ret == NULL) {
			RETVAL = &PL_sv_undef;
		} else {
			RETVAL = SvREFCNT_inc(*ret);
		}
	OUTPUT:
		RETVAL

SV *
res_date(self)
	      SV *self
	CODE:
		HV *hash;
		SV **ret;

		hash = (HV *) SvRV(self);
		ret = hv_fetch(hash, DATE, DATE_LEN, 0);
		if (ret == NULL) {
			RETVAL = &PL_sv_undef;
		} else {
			RETVAL = SvREFCNT_inc(*ret);
		}
	OUTPUT:
		RETVAL

SV *
res_rank(self)
	      SV *self
	CODE:
		HV *hash;
		SV **ret;

		hash = (HV *) SvRV(self);
		ret = hv_fetch(hash, RANK, RANK_LEN, 0);
		if (ret == NULL) {
			RETVAL = &PL_sv_undef;
		} else {
			RETVAL = SvREFCNT_inc(*ret);
		}
	OUTPUT:
		RETVAL

SV *
res_docid(self)
	      SV *self
	CODE:
		HV *hash;
		SV **ret;

		hash = (HV *) SvRV(self);
		ret = hv_fetch(hash, DOCID, DOCID_LEN, 0);
		if (ret == NULL) {
			RETVAL = &PL_sv_undef;
		} else {
			RETVAL = SvREFCNT_inc(*ret);
		}
	OUTPUT:
		RETVAL

SV *
res_idxid(self)
	      SV *self
	CODE:
		HV *hash;
		SV **ret;

		hash = (HV *) SvRV(self);
		ret = hv_fetch(hash, IDXID, IDXID_LEN, 0);
		if (ret == NULL) {
			RETVAL = &PL_sv_undef;
		} else {
			RETVAL = SvREFCNT_inc(*ret);
		}
	OUTPUT:
		RETVAL