#include<string.h>
#include "ruby.h"
#include "narray.h"

/* for compatibility with ruby 1.6 */
#ifndef RARRAY_PTR
#define RARRAY_PTR(a) (RARRAY(a)->ptr)
#endif
#ifndef RARRAY_LEN
#define RARRAY_LEN(a) (RARRAY(a)->len)
#endif
#ifndef RFLOAT_VALUE
#define RFLOAT_VALUE(v) (RFLOAT(v)->value)
#endif
#ifndef StringValueCStr
#define StringValueCStr(s) STR2CSTR(s)
#endif

VALUE mHE5=0;

/*  functions  */
char       *hdfeos5_obj2ccharary(VALUE, int, int);
int        *hdfeos5_obj2cintary(VALUE);
long       *hdfeos5_obj2clongary(VALUE);
float      *hdfeos5_obj2cfloatary(VALUE);
signed long long   *hdfeos5_obj2csint64ary(VALUE);
unsigned long long *ohdfeos5_obj2cunsint64ary(VALUE);

void hdfeos5_freeccharary(char *);
void hdfeos5_freecintary(int *);
void hdfeos5_freeclongary(long *);
void hdfeos5_freecfloatary(float *);
void hdfeos5_freecsint64ary(signed long long *);
void hdfeos5_freecunsint64ary(unsigned long long *);

static char      *ary2ccharary(VALUE, int, int);
static int       *ary2cintary(VALUE);
static long      *ary2clongary(VALUE);
static float     *ary2cfloatary(VALUE);
static signed long long    *ary2csint64ary(VALUE);
static unsigned long long  *ary2cunsint64ary(VALUE);

static int       *na2cintary(VALUE);
static long      *na2clongary(VALUE);
static float     *na2cfloatary(VALUE);
static signed long long   *na2csint64ary(VALUE);
static unsigned long long *na2cunsint64ary(VALUE);

/*  defines  */
#define BE_INTEGER(x) ((int)(NUM2INT(rb_Integer(x))))
#define BE_LONG(x)    ((long)(NUM2INT(rb_Integer(x))))
#define BE_FLOAT(x)   (RFLOAT_VALUE(rb_Float(x)))

/*
 * hdfeos5_obj2cxxxary() : convert ruby object to c xxx type array
 *   hdfeos5_obj2ccharary()
 *   hdfeos5_obj2cintary()
 *   hdfeos5_obj2clongary()
 *   hdfeos5_obj2cfloatary()
 */
char *
hdfeos5_obj2ccharary(VALUE src,int size,int len)
{
    char *rtn;

    switch (TYPE(src)){
    case T_ARRAY:
        rtn = ary2ccharary(src, size, len);
        break;
    default:
        rb_raise(rb_eTypeError, "expect int array");
        break;
    }
    return rtn;
}

int *
hdfeos5_obj2cintary(VALUE src)
{
    VALUE chk;
    int *rtn;

    switch (TYPE(src)){
    case T_DATA:
        chk = rb_obj_is_kind_of(src, cNArray);
        if (chk == Qfalse) {
            rb_raise(rb_eTypeError, "expect int array");
        }
        rtn = na2cintary(src);
        break;
    case T_ARRAY:
        rtn = ary2cintary(src);
        break;
    default:
        rb_raise(rb_eTypeError, "expect int array");
        break;
    }

    return rtn;
}

long *
hdfeos5_obj2clongary(VALUE src)
{
    VALUE chk;
    long *rtn;

    switch (TYPE(src)){
    case T_DATA:
        chk = rb_obj_is_kind_of(src, cNArray);
        if (chk == Qfalse) {
            rb_raise(rb_eTypeError, "expect int array");
        }
        rtn = na2clongary(src);
        break;
    case T_ARRAY:
        rtn = ary2clongary(src);
        break;
    default:
        rb_raise(rb_eTypeError, "expect int array");
        break;
    }
    return rtn;
}

signed long long *
hdfeos5_obj2csint64ary(VALUE src)
{
    VALUE chk;
    signed long long *rtn;

    switch (TYPE(src)){
    case T_DATA:
        chk = rb_obj_is_kind_of(src, cNArray);
        if (chk == Qfalse) {
            rb_raise(rb_eTypeError, "expect int array");
        }
        rtn = na2csint64ary(src);
        break;
    case T_ARRAY:
        rtn = ary2csint64ary(src);
        break;
    default:
        rb_raise(rb_eTypeError, "expect int array");
        break;
    }
    return rtn;
}

unsigned long long *
hdfeos5_obj2cunsint64ary(VALUE src)
{
    VALUE chk;
    unsigned long long *rtn;

    switch (TYPE(src)){
    case T_DATA:
        chk = rb_obj_is_kind_of(src, cNArray);
        if (chk == Qfalse) {
            rb_raise(rb_eTypeError, "expect int array");
        }
        rtn = na2cunsint64ary(src);
        break;
    case T_ARRAY:
        rtn = ary2cunsint64ary(src);
        break;
    default:
        rb_raise(rb_eTypeError, "expect int array");
        break;
    }
    return rtn;
}

float *
hdfeos5_obj2cfloatary(VALUE src)
{
    VALUE chk;
    float *rtn;
    char *klass;
    VALUE klass_tmp;
    VALUE rmiss;


    switch (TYPE(src)){
    case T_OBJECT:
	klass_tmp = rb_funcall(rb_funcall(src, rb_intern("class"),0),
				     rb_intern("to_s"),0);
	klass = StringValueCStr(klass_tmp);
	if (strncmp(klass,"NArrayMiss",10) != 0) {
	    rb_raise(rb_eTypeError, "a numeric array expected");
	}
	rmiss = rb_funcall(mHE5,rb_intern("glrget"),1,rb_str_new2("rmiss"));
	src = rb_funcall( src, rb_intern("to_na"), 1, rmiss );
    case T_DATA:
        chk = rb_obj_is_kind_of(src, cNArray);
        rtn = na2cfloatary(src);
        break;
    case T_ARRAY:
        rtn = ary2cfloatary(src);
        break;
    default:
        rb_raise(rb_eTypeError, "expect float array");
        break;
    }
    return rtn;
}

/*
 * hdfeos5_freecxxxary() : free c xxx type array
 *                     allocated by ary2cxxxary() or na2cxxxary()
 *   hdfeos5_freeccharary()
 *   hdfeos5_freecintary()
 *   hdfeos5_freeclongary()
 *   hdfeos5_freecfloatary()
 */

void
hdfeos5_freeccharary(cary)
    char *cary;
{
    if ( cary != NULL ) {
	free(cary);
    }
    return;
}

void
hdfeos5_freecintary(int *cary)
{
    if ( cary != NULL ) {
	free(cary);
    }
    return;
}

void
hdfeos5_freeclongary(long *cary)
{
    if ( cary != NULL ) {
	free(cary);
    }
    return;
}

void
hdfeos5_freecsint64ary(signed long long *cary)
{
    if ( cary != NULL ) {
	free(cary);
    }
    return;
}

void
hdfeos5_freecunsint64ary(unsigned long long *cary)
{
    if ( cary != NULL ) {
	free(cary);
    }
    return;
}

void
hdfeos5_freecfloatary(cary)
    float *cary;
{
    if ( cary != NULL ) {
	free(cary);
    }
    return;
}

/*
 *  ary2cxxxary() : convert Array object to c xxx type array
 *                  called by obj2cxxxary()
 *   ary2ccharary()
 *   ary2cintary()
 *   ary2clongary()
 *   ary2cfloatary()
 */
static char *
ary2ccharary(VALUE src,int size,int charlen)
{
    VALUE *ptr;
    long len, i;
    char *rtn, *wk, *rwk;

    Check_Type(src, T_ARRAY);

    len = RARRAY_LEN(src);
    ptr = RARRAY_PTR(src);

    rtn = ALLOC_N(char, size);
    memset(rtn, '\0', size);
    wk = rtn;
    for (i = 0; i < len; i++) {
	rwk = StringValuePtr(ptr[i]);
        strncpy(wk, rwk, charlen);
    }
    return rtn;
}

static int *
ary2cintary(VALUE src)
{
    VALUE *ptr;
    int len, i;
    int *rtn;

    Check_Type(src, T_ARRAY);

    len = RARRAY_LEN(src);
    ptr = RARRAY_PTR(src);

    rtn = ALLOC_N(int, len);

    for (i = 0; i < len; i++) {
        rtn[i] = BE_INTEGER(ptr[i]);
    }

    return rtn;
}

static long *
ary2clongary(VALUE src)
{
    VALUE *ptr;
    int len, i;
    long *rtn;

    Check_Type(src, T_ARRAY);

    len = RARRAY_LEN(src);
    ptr = RARRAY_PTR(src);

    rtn = ALLOC_N(long, len);

    for (i = 0; i < len; i++) {
        rtn[i] = BE_LONG(ptr[i]);
    }

    return rtn;
}

static signed long long *
ary2csint64ary(VALUE src)
{
    VALUE *ptr;
    int len, i;
    signed long long *rtn;

    Check_Type(src, T_ARRAY);

    len = RARRAY_LEN(src);
    ptr = RARRAY_PTR(src);

    rtn = ALLOC_N(signed long long, len);

    for (i = 0; i < len; i++) {
        rtn[i] = BE_LONG(ptr[i]);
    }

    return rtn;
}

static unsigned long long *
ary2cunsint64ary(VALUE src)
{
    VALUE *ptr;
    int len, i;
    unsigned long long *rtn;

    Check_Type(src, T_ARRAY);

    len = RARRAY_LEN(src);
    ptr = RARRAY_PTR(src);

    rtn = ALLOC_N(unsigned long long, len);

    for (i = 0; i < len; i++) {
        rtn[i] = BE_LONG(ptr[i]);
    }

    return rtn;
}

static float *
ary2cfloatary(VALUE src)
{
    VALUE *ptr;
    int len, i;
    float *rtn;

    Check_Type(src, T_ARRAY);

    len = RARRAY_LEN(src);
    ptr = RARRAY_PTR(src);

    rtn = ALLOC_N(float , len);

    for (i = 0; i < len; i++) {
	rtn[i] = BE_FLOAT(ptr[i]);
    }

    return rtn;
}

#define NA2PTR(obj) ((NA*)DATA_PTR(obj))->bna->ptr
#define NA2LEN(obj) ((NA*)DATA_PTR(obj))->bna->len

/*
 *  na2cxxxary() : convert NArray object to c xxx type array
 *                 called by obj2cxxxary()
 *   na2cintary() : not implemented
 *   na2clongary() : not implemented
 *   na2cfloatary()
 *   na2ccomplexary() : not implemented
 *   na2clogicalary()
 */
static float *
na2cfloatary(src)
    VALUE src;
{
    VALUE chk;
    int len, i;
    float *rtn;
    float *ptr;
    struct NARRAY *na;

    chk = rb_obj_is_kind_of(src, cNArray);
    if (chk == Qfalse) {
        rb_raise(rb_eTypeError, "expect NArray");
    }

    src = na_cast_object(src, NA_SFLOAT);
    GetNArray(src, na);
    len = na->total;
    ptr = (float *)NA_PTR(na, 0);

    rtn = ALLOC_N(float, len);
    for (i = 0; i < len; i++) {
	rtn[i] = (float)ptr[i];
    }
    
    return rtn;
}

static int *
na2cintary(src)
    VALUE src;
{
    VALUE chk;
    int len, i;
    int *rtn;
    int32_t *ptr;
    struct NARRAY *na;

    chk = rb_obj_is_kind_of(src, cNArray);
    if (chk == Qfalse) {
        rb_raise(rb_eTypeError, "expect NArray");
    }

    src = na_cast_object(src, NA_LINT);
    GetNArray(src, na);
    len = na->total;
    ptr = (int32_t *)NA_PTR(na, 0);

    rtn = ALLOC_N(int, len);

    for (i = 0; i < len; i++) {
	rtn[i] = (int)ptr[i];
    }
    
    return rtn;
}

static long *
na2clongary(VALUE src)
{
    VALUE chk;
    int len, i;
    long *rtn;
    int32_t *ptr;
    struct NARRAY *na;

    chk = rb_obj_is_kind_of(src, cNArray);
    if (chk == Qfalse) {
        rb_raise(rb_eTypeError, "expect NArray");
    }

    src = na_cast_object(src, NA_LINT);
    GetNArray(src, na);
    len = na->total;
    ptr = (int32_t *)NA_PTR(na, 0);

    rtn = ALLOC_N(long, len);

    for (i = 0; i < len; i++) {
	rtn[i] = (long)ptr[i];
    }
    
    return rtn;
}

static signed long long *
na2csint64ary(VALUE src)
{
    VALUE chk;
    int len, i;
    signed long long *rtn;
    signed long long *ptr;
    struct NARRAY *na;

    chk = rb_obj_is_kind_of(src, cNArray);
    if (chk == Qfalse) {
        rb_raise(rb_eTypeError, "expect NArray");
    }

    src = na_cast_object(src, NA_LINT);
    GetNArray(src, na);
    len = na->total;
    ptr = (signed long long *)NA_PTR(na, 0);

    rtn = ALLOC_N(signed long long, len);

    for (i = 0; i < len; i++) {
	rtn[i] = (signed long long)ptr[i];
    }
    
    return rtn;
}

static unsigned long long *
na2cunsint64ary(VALUE src)
{
    VALUE chk;
    int len, i;
    unsigned long long *rtn;
    unsigned long long *ptr;
    struct NARRAY *na;

    chk = rb_obj_is_kind_of(src, cNArray);
    if (chk == Qfalse) {
        rb_raise(rb_eTypeError, "expect NArray");
    }

    src = na_cast_object(src, NA_LINT);
    GetNArray(src, na);
    len = na->total;
    ptr = (unsigned long long *)NA_PTR(na, 0);

    rtn = ALLOC_N(unsigned long long, len);

    for (i = 0; i < len; i++) {
	rtn[i] = (unsigned long long)ptr[i];
    }
    
    return rtn;
}
