/*
 * Sary::Builder - a builder class for Sary
 *
 * $Id: builder.c,v 1.4 2001/08/30 19:21:07 kazuma Exp $
 *
 * Copyright (C) 2000 TAKAOKA Kazuma <kazuma-t@is.aist-nara.ac.jp>
 * 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_Builder(obj, dat) Data_Get_Struct(obj, SaryBuilder, dat)

static VALUE BuilderClass;

/* For SaryBuilder class. */
static VALUE rsbuilder_s_new(int argc, VALUE *argv, VALUE klass);
static void  rsbuilder_destroy(SaryBuilder *builder);
static VALUE rsbuilder_sort(int argc, VALUE *argv, VALUE klass);
static VALUE rsbuilder_block_sort(int argc, VALUE *argv, VALUE klass);
static VALUE rsbuilder_set_block_size(VALUE klass, VALUE size);
static VALUE rsbuilder_set_nthreads(VALUE klass, VALUE nthreads);

static void dummy_progress(SaryProgress *progress);
static void progress(SaryProgress *progress);

static VALUE rsbuilder_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 rsbuilder_initialize(int argc, VALUE *argv, VALUE klass)
{
    SaryBuilder *builder;
    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)
        builder = sary_builder_new(file_name_ptr);
    else {
        Check_SafeStr(array_name);
        array_name_ptr = STR2CSTR(array_name);
        builder = sary_builder_new2(file_name_ptr, array_name_ptr);
    }
    if (builder == NULL)
	rb_raise(rb_eIOError, g_strerror(errno));

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

    return klass;
}

static void rsbuilder_destroy(SaryBuilder *builder)
{
    sary_builder_destroy(builder);
}

static VALUE rsb_sort(int argc, VALUE *argv, VALUE klass,
		      gboolean (*func)(SaryBuilder*))
{
    SaryBuilder *builder;
    VALUE block;

    rb_scan_args(argc, argv, "00&", &block);

    GET_Builder(klass, builder);

    if (block == Qnil) {
	sary_builder_connect_progress(builder, dummy_progress, NULL);
    } else {
	sary_builder_connect_progress(builder, progress, NULL);
    }

    if (func(builder) == FALSE) {
	rb_raise(rb_eRuntimeError, g_strerror(errno));
    }

    return klass;
}

static VALUE rsbuilder_sort(int argc, VALUE *argv, VALUE klass)
{
    return rsb_sort(argc, argv, klass, sary_builder_sort);
}

static VALUE rsbuilder_block_sort(int argc, VALUE *argv, VALUE klass)
{
    return rsb_sort(argc, argv, klass, sary_builder_block_sort);
}

static VALUE rsbuilder_set_block_size(VALUE klass, VALUE size)
{
    SaryBuilder *builder;

    GET_Builder(klass, builder);
    sary_builder_set_block_size(builder, NUM2INT(size));

    return klass;
}

static void dummy_progress(SaryProgress *progress)
{
    /* do nothing */
}

static void progress(SaryProgress *progress)
{
    VALUE args;
    SaryInt cur_percentage, prev_percentage;

    gint current  = progress->current  + 1;
    gint previous = progress->previous + 1;
    gint total    = progress->total    + 1;

    cur_percentage = (gint)((gdouble)current  * 100 / total);
    prev_percentage = (gint)((gdouble)previous * 100 / total);
    if (cur_percentage > prev_percentage || progress->is_finished) {    
	args = rb_ary_new3(4, rb_str_new2(progress->task),
			   INT2NUM(current), INT2NUM(total),
			   (progress->is_finished) ? Qtrue : Qfalse);
	rb_yield(args);
    }
}

static VALUE rsbuilder_set_nthreads(VALUE klass, VALUE nthreads)
{
    SaryBuilder *builder;

    GET_Builder(klass, builder);
    sary_builder_set_nthreads(builder, NUM2INT(nthreads));

    return klass;
}

/*
 * Initialize class
 */
void
Init_sarybuilder(VALUE module)
{
    BuilderClass
	= rb_define_class_under(module, "Builder", rb_cObject);

    rb_define_singleton_method(BuilderClass, "new", rsbuilder_s_new, -1);

    rb_define_method(BuilderClass, "initialize", rsbuilder_initialize, -1);
    rb_define_method(BuilderClass, "sort", rsbuilder_sort, -1);
    rb_define_method(BuilderClass, "block_sort", rsbuilder_block_sort, -1);
    rb_define_method(BuilderClass, "set_block_size",
		     rsbuilder_set_block_size, 1);
    rb_define_method(BuilderClass, "set_nthreads",
		     rsbuilder_set_nthreads, 1);
}
