/*
  block_source.c
  Ruby/GSL: Ruby extension library for GSL (GNU Scientific Library)
    (C) Copyright 2001-2004 by Yoshiki Tsunesada

  Ruby/GSL is free software: you can redistribute it and/or modify it
  under the terms of the GNU General Public License.
  This library is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY.
*/

static VALUE FUNCTION(rb_gsl_block,new)(VALUE klass, VALUE nn)
{
  GSL_TYPE(gsl_block) *block = NULL;
  CHECK_FIXNUM(nn);
  block = FUNCTION(gsl_block,alloc)(FIX2INT(nn));
  return Data_Wrap_Struct(klass, 0, FUNCTION(gsl_block,free), block);
}

static VALUE FUNCTION(rb_gsl_block,calloc)(VALUE klass, VALUE nn)
{
  GSL_TYPE(gsl_block) *block = NULL;
  CHECK_FIXNUM(nn);
  block = FUNCTION(gsl_block,calloc)(FIX2INT(nn));
  return Data_Wrap_Struct(klass, 0, FUNCTION(gsl_block,free), block);
}

static VALUE FUNCTION(rb_gsl_block,size)(VALUE obj)
{
  GSL_TYPE(gsl_block) *block = NULL;
  Data_Get_Struct(obj, GSL_TYPE(gsl_block), block);
  return INT2FIX(block->size);
}

static VALUE FUNCTION(rb_gsl_block,fwrite)(VALUE obj, VALUE io)
{
  GSL_TYPE(gsl_block) *h = NULL;
  FILE *f = NULL;
  int status, flag = 0;
  Data_Get_Struct(obj, GSL_TYPE(gsl_block), h);
  f = rb_gsl_open_writefile(io, &flag);
  status = FUNCTION(gsl_block,fwrite)(f, h);
  if (flag == 1) fclose(f);
  return INT2FIX(status);
}

static VALUE FUNCTION(rb_gsl_block,fread)(VALUE obj, VALUE io)
{
  GSL_TYPE(gsl_block) *h = NULL;
  FILE *f = NULL;
  int status, flag = 0;
  Data_Get_Struct(obj, GSL_TYPE(gsl_block), h);
  f = rb_gsl_open_readfile(io, &flag);
  status = FUNCTION(gsl_block,fread)(f, h);
  if (flag == 1) fclose(f);
  return INT2FIX(status);
}

static VALUE FUNCTION(rb_gsl_block,fprintf)(int argc, VALUE *argv, VALUE obj)
{
  GSL_TYPE(gsl_block) *h = NULL;
  FILE *fp = NULL;
  int status, flag = 0;
  if (argc != 1 && argc != 2) 
    rb_raise(rb_eArgError, 
	     "wrong number of arguments (%d for 1 or 2)", argc);
  Data_Get_Struct(obj, GSL_TYPE(gsl_block), h);
  fp = rb_gsl_open_writefile(argv[0], &flag);
  if (argc == 2) {
    Check_Type(argv[1], T_STRING);
    status = FUNCTION(gsl_block,fprintf)(fp, h, STR2CSTR(argv[1]));
  } else {
    status = FUNCTION(gsl_block,fprintf)(fp, h, "%g");
  }
  if (flag == 1) fclose(fp);
  return INT2FIX(status);
}

static VALUE FUNCTION(rb_gsl_block,printf)(int argc, VALUE *argv, VALUE obj)
{
  GSL_TYPE(gsl_block) *h = NULL;
  int status;
  Data_Get_Struct(obj, GSL_TYPE(gsl_block), h);
  if (argc == 1) {
    Check_Type(argv[0], T_STRING);
    status = FUNCTION(gsl_block,fprintf)(stdout, h, STR2CSTR(argv[0]));
  } else {
    status = FUNCTION(gsl_block,fprintf)(stdout, h, "%g");
  }
  return INT2FIX(status);
}

static VALUE FUNCTION(rb_gsl_block,fscanf)(VALUE obj, VALUE io)
{
  GSL_TYPE(gsl_block) *h = NULL;
  FILE *fp = NULL;
  int status, flag = 0;
  Data_Get_Struct(obj, GSL_TYPE(gsl_block), h);
  fp = rb_gsl_open_readfile(io, &flag);
  status = FUNCTION(gsl_block,fscanf)(fp, h);
  if (flag == 1) fclose(fp);
  return INT2FIX(status);
}

#ifdef BASE_DOUBLE
#define SHOW_ELM 6
#define PRINTF_FORMAT "%4.3e "
#else
#define SHOW_ELM 15
#define PRINTF_FORMAT "%d "
#endif

static VALUE FUNCTION(rb_gsl_block,to_s)(VALUE obj)
{
  GSL_TYPE(gsl_block) *v = NULL;
  char buf[32];
  size_t i;
  VALUE str;
  Data_Get_Struct(obj, GSL_TYPE(gsl_block), v);
  str = rb_str_new2("[ ");
  for (i = 0; i < v->size; i++) {
    sprintf(buf,  PRINTF_FORMAT, v->data[i]);
    rb_str_cat(str, buf, strlen(buf));
    if (i == SHOW_ELM && i != v->size-1) {
      strcpy(buf, "... ");
      rb_str_cat(str, buf, strlen(buf));
      break;
    }
  }
  sprintf(buf, "]");
  rb_str_cat(str, buf, strlen(buf));
  return str;
}
#undef SHOW_ELM
#undef PRINTF_FORMAT

static VALUE FUNCTION(rb_gsl_block,inspect)(VALUE obj)
{
  VALUE str;
  char buf[64];
  sprintf(buf, "%s: \n", rb_class2name(CLASS_OF(obj)));
  str = rb_str_new2(buf);
  return rb_str_concat(str, FUNCTION(rb_gsl_block,to_s)(obj));
}

#ifdef BASE_DOUBLE
#define C_TO_VALUE rb_float_new
#define NUMCONV NUM2DBL
#else
#define C_TO_VALUE INT2FIX
#define NUMCONV FIX2INT
#endif

static VALUE FUNCTION(rb_gsl_block,get)(VALUE obj, VALUE ii)
{
  GSL_TYPE(gsl_block) *b;
  size_t i;
  CHECK_FIXNUM(ii);
  Data_Get_Struct(obj, GSL_TYPE(gsl_block), b);
  i = FIX2INT(ii);
  return C_TO_VALUE((BASE) b->data[i]);
}

static VALUE FUNCTION(rb_gsl_block,set)(VALUE obj, VALUE ii, VALUE xx)
{
  GSL_TYPE(gsl_block) *b;
  BASE x;
  size_t i;
  CHECK_FIXNUM(ii);
  i = FIX2INT(ii);
  x = NUMCONV(xx);
  Data_Get_Struct(obj, GSL_TYPE(gsl_block), b);
  b->data[i] = x;
  return obj;
}

#undef C_TO_VALUE
#undef NUMCONV

void FUNCTION(Init_gsl_block,init)(VALUE module)
{
  rb_define_singleton_method(GSL_TYPE(cgsl_block), "new", 
			     FUNCTION(rb_gsl_block,new), 1);
  rb_define_singleton_method(GSL_TYPE(cgsl_block), "alloc", 
			     FUNCTION(rb_gsl_block,new), 1);
  rb_define_singleton_method(GSL_TYPE(cgsl_block), "calloc", 
			     FUNCTION(rb_gsl_block,calloc), 1);
  rb_define_method(GSL_TYPE(cgsl_block), "size", FUNCTION(rb_gsl_block,size), 0);
  rb_define_method(GSL_TYPE(cgsl_block), "fwrite", FUNCTION(rb_gsl_block,fwrite), 1);
  rb_define_method(GSL_TYPE(cgsl_block), "fread", FUNCTION(rb_gsl_block,fread), 1);
  rb_define_method(GSL_TYPE(cgsl_block), "fprintf", FUNCTION(rb_gsl_block,fprintf), -1);
  rb_define_method(GSL_TYPE(cgsl_block), "printf", FUNCTION(rb_gsl_block,printf), -1);
  rb_define_method(GSL_TYPE(cgsl_block), "fscanf", FUNCTION(rb_gsl_block,fscanf), 1);

  rb_define_method(GSL_TYPE(cgsl_block), "inspect", FUNCTION(rb_gsl_block,inspect), 0);
  rb_define_method(GSL_TYPE(cgsl_block), "to_s", FUNCTION(rb_gsl_block,to_s), 0);
  rb_define_method(GSL_TYPE(cgsl_block), "get", FUNCTION(rb_gsl_block,get), 1);
  rb_define_alias(GSL_TYPE(cgsl_block), "[]", "get");
  rb_define_method(GSL_TYPE(cgsl_block), "set", FUNCTION(rb_gsl_block,set), 2);
  rb_define_alias(GSL_TYPE(cgsl_block), "[]=", "set");
}
