/*
 * Sary::Searcher - a searcher class for Sary
 *
 * $Id: searcher.c,v 1.1 2003/05/09 09:17:41 rug Exp $
 *
 * Copyright (C) 2000 TAKAOKA Kazuma <kazuma-t@is.aist-nara.ac.jp>
 * Copyright (C) 2002 Hiroyuki Komatsu <komatsu@taiyaki.org>
 * All right reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 * 02111-1307  USA
 */

#include <stddef.h>
#include <errno.h>
#include "sary.h"
#include "ruby.h"

#define GET_Searcher(obj, dat) Data_Get_Struct(obj, SarySearcher, dat)

static VALUE SearcherClass;

/* For Searcher class. */
static VALUE rsearcher_s_new(int argc, VALUE *argv, VALUE klass);
static void  rsearcher_destroy(SarySearcher *searcher);
static VALUE rsearcher_search(VALUE klass, VALUE pattern);
static VALUE rsearcher_multi_search(VALUE klass, VALUE pattern_array);
static VALUE rsearcher_isearch(VALUE klass, VALUE pattern, VALUE len);
static VALUE rsearcher_isearch_reset(VALUE klass);
static VALUE rsearcher_icase_search(VALUE klass, VALUE pattern);
static VALUE rsearcher_get_next_context_lines(int argc, VALUE *argv,
					    VALUE klass);
static VALUE rsearcher_get_next_tagged_region(VALUE klass,
					    VALUE start_tag, VALUE end_tag);
static VALUE rsearcher_each_context_lines(int argc, VALUE *argv, VALUE klass);
static VALUE rsearcher_each_tagged_region(VALUE klass,
					VALUE start_tag, VALUE end_tag);
static VALUE rsearcher_count_occurrences(VALUE klass);
static VALUE rsearcher_sort_occurence(VALUE klass);
static VALUE rsearcher_enable_cache(VALUE klass);


/*
 * Initilize Searcher class instance.
 */
static VALUE
rsearcher_s_new(int argc, VALUE *argv, VALUE klass)
{
    NEWOBJ(data, struct RData);
    OBJSETUP(data, klass, T_DATA);

    rb_obj_call_init((VALUE)data, argc, argv);

    return (VALUE)data;
}

static VALUE
rsearcher_initialize(int argc, VALUE *argv, VALUE klass)
{
    SarySearcher *searcher;
    char *file_name_ptr;
    char *array_name_ptr;
    VALUE file_name, array_name;
  
    rb_scan_args(argc, argv, "11", &file_name, &array_name);

    Check_SafeStr(file_name);
    file_name_ptr = STR2CSTR(file_name);
  
    if (array_name == Qnil)
        searcher = sary_searcher_new(file_name_ptr);
    else {
        Check_SafeStr(array_name);
        array_name_ptr = STR2CSTR(array_name);
        searcher = sary_searcher_new2(file_name_ptr, array_name_ptr);
    }
    if (searcher == NULL)
	rb_raise(rb_eIOError, g_strerror(errno));

    Check_Type(klass, T_DATA);
    RDATA(klass)->dfree = (RUBY_DATA_FUNC)rsearcher_destroy;
    RDATA(klass)->dmark = (RUBY_DATA_FUNC)0;
    DATA_PTR(klass) = searcher;

    return klass;
}

/*
 * Destroy instance.
 */
static void
rsearcher_destroy(SarySearcher *searcher)
{
    sary_searcher_destroy(searcher);
}

/*
 * Instance methods.
 */
static VALUE
rsearcher_search(VALUE klass, VALUE pattern)
{
    SarySearcher *searcher;
    char *pat;
    int count, len;

    GET_Searcher(klass, searcher);

    Check_SafeStr(pattern);
    pat = str2cstr(pattern, &len);

    if (sary_searcher_search2(searcher, pat, len))
	return Qtrue;
    else
	return Qfalse;
}

static VALUE
rsearcher_multi_search(VALUE klass, VALUE pattern_array)
{
    SarySearcher *searcher;
    char **pat;
    int count, len, i;
    VALUE pattern;

    GET_Searcher(klass, searcher);

    len = RARRAY(pattern_array)->len;
    if (len == 0) {
      return Qfalse;
    }

    pat = ALLOCA_N(char*, len);
    for (i = 0; i < len; i++) {
	pattern = rb_ary_entry(pattern_array, (long)i);
	Check_SafeStr(pattern);
	pat[i] = STR2CSTR(pattern);
    }

    if (sary_searcher_multi_search(searcher, pat, len))
	return Qtrue;
    else
	return Qfalse;
}

static VALUE
rsearcher_isearch(VALUE klass, VALUE pattern, VALUE len)
{
    SarySearcher *searcher;
    char *pat;
    int count;

    GET_Searcher(klass, searcher);

    Check_SafeStr(pattern);
    pat = STR2CSTR(pattern);

    if (sary_searcher_isearch(searcher, pat, NUM2INT(len)))
	return Qtrue;
    else
	return Qfalse;
}

static VALUE
rsearcher_isearch_reset(VALUE klass)
{
    SarySearcher *searcher;

    GET_Searcher(klass, searcher);

    sary_searcher_isearch_reset(searcher);

    return klass;
}

static VALUE
rsearcher_icase_search(VALUE klass, VALUE pattern)
{
    SarySearcher *searcher;
    char *pat;
    int count, len;

    GET_Searcher(klass, searcher);

    Check_SafeStr(pattern);
    pat = str2cstr(pattern, &len);

    if (sary_searcher_icase_search2(searcher, pat, len))
	return Qtrue;
    else
	return Qfalse;
}

static VALUE
rsearcher_get_next_context_lines(int argc, VALUE *argv, VALUE klass)
{
    SarySearcher *searcher;
    VALUE bkwrd, frwrd;
    int bk, fr, len;
    char *region;

    GET_Searcher(klass, searcher);
    if (sary_searcher_count_occurrences(searcher) == 0)
  	return Qnil;

    rb_scan_args(argc, argv, "02", &bkwrd, &frwrd);
    bk = (bkwrd == Qnil) ? 0 : NUM2INT(bkwrd);
    fr = (frwrd == Qnil) ? 0 : NUM2INT(frwrd);

    region = sary_searcher_get_next_context_lines2(searcher, bk, fr, &len);

    if (region == NULL)
	return Qnil;
    else
	return rb_str_new(region, len);
}

static VALUE
rsearcher_get_next_tagged_region(VALUE klass,
			       VALUE start_tag, VALUE end_tag)
{
    SarySearcher *searcher;
    char* stag, *etag, *region;
    int slen, elen, rlen;

    GET_Searcher(klass, searcher);
    if (sary_searcher_count_occurrences(searcher) == 0)
  	return Qnil;

    Check_SafeStr(start_tag);
    Check_SafeStr(end_tag);
    stag = rb_str2cstr(start_tag, &slen);
    etag = rb_str2cstr(end_tag, &elen);

    region = sary_searcher_get_next_tagged_region2(searcher, stag, slen,
					    etag, elen, &rlen);

    if (region == NULL)
	return Qnil;
    else
	return rb_str_new(region, rlen);
}

static VALUE
rsearcher_each_context_lines(int argc, VALUE *argv, VALUE klass)
{
    VALUE str;
 
    while (!NIL_P(str = rsearcher_get_next_context_lines(argc, argv, klass)))
	rb_yield(str);

    return klass;
}

static VALUE
rsearcher_each_tagged_region(VALUE klass,	VALUE start_tag, VALUE end_tag)
{
    VALUE str;

    while (!NIL_P(str = rsearcher_get_next_tagged_region(klass,
						       start_tag, end_tag)))
	rb_yield(str);

    return klass;
}

static VALUE
rsearcher_count_occurrences(VALUE klass)
{
    SarySearcher *searcher;

    GET_Searcher(klass, searcher);

    return INT2NUM(sary_searcher_count_occurrences(searcher));
}

static VALUE
rsearcher_enable_cache(VALUE klass)
{
    SarySearcher *searcher;

    GET_Searcher(klass, searcher);
    sary_searcher_enable_cache(searcher);

    return klass;
}

static VALUE
rsearcher_sort_occurrences(VALUE klass)
{
    SarySearcher *searcher;

    GET_Searcher(klass, searcher);
    if (sary_searcher_count_occurrences(searcher) != 0)
	sary_searcher_sort_occurrences(searcher);

    return klass;
}


/*
 * Initialize class
 */
void
Init_sarysearcher(VALUE module)
{
    SearcherClass = rb_define_class_under(module, "Searcher", rb_cObject);

    rb_define_singleton_method(SearcherClass, "new", rsearcher_s_new, -1);
    rb_define_method(SearcherClass, "initialize", rsearcher_initialize, -1);
    rb_define_method(SearcherClass, "search", rsearcher_search, 1);
    rb_define_method(SearcherClass, "multi_search", rsearcher_multi_search, 1);
    rb_define_method(SearcherClass, "isearch", rsearcher_isearch, 2);
    rb_define_method(SearcherClass, "isearch_reset",
		     rsearcher_isearch_reset, 0);
    rb_define_method(SearcherClass, "icase_search",
		     rsearcher_icase_search, 1);
    rb_define_method(SearcherClass, "get_next_context_lines",
		     rsearcher_get_next_context_lines, -1);
    rb_define_method(SearcherClass, "get_next_context_line",
		     rsearcher_get_next_context_lines, -1);
    rb_define_method(SearcherClass, "get_next_tagged_region",
		     rsearcher_get_next_tagged_region, 2);
    rb_define_method(SearcherClass, "each_context_lines",
		     rsearcher_each_context_lines, -1);
    rb_define_method(SearcherClass, "each_context_line",
		     rsearcher_each_context_lines, -1);
    rb_define_method(SearcherClass, "each_tagged_region",
		     rsearcher_each_tagged_region, 2);
    rb_define_method(SearcherClass, "count_occurrences",
		     rsearcher_count_occurrences, 0);
    rb_define_method(SearcherClass, "sort_occurrences",
		     rsearcher_sort_occurrences, 0);
    rb_define_method(SearcherClass, "enable_cache",
		     rsearcher_enable_cache, 0);
}
