/**********************************************************************
 
	Copyright (C) 2005- Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	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.

**********************************************************************/

#include	"memory_debug.h"
#include	"xl.h"
#include	"matrix.h"
#include	"memory_routine.h"
#include	"utils.h"
#include	"change_endian.h"


int mxt_string_get_size(MATRIX_DATA_TYPE *,void * d);
void * mxt_string_sexp2md(int * cpy,struct matrix_data_type*,XL_SEXP * s);
XL_SEXP * mxt_string_md2sexp(struct matrix_data_type*,void * d);
int mxt_string_cmp(struct matrix_data_type*,void*,void*);
int mxt_stringv_cmp(struct matrix_data_type*,void*,void*);
int mxt_stringv_get_size(MATRIX_DATA_TYPE *,void * d);
void mxt_stringv_copy(struct matrix_data_type*,void*,void*,int,void*);
void * mxt_alloc_stringv_copy(struct matrix_data_type*,void*,int,void*);
void * mxt_alloc_stringv_data(struct matrix_data_type*,int,void*,void*,
			      char*,int);
void mxt_free_stringv_data(struct matrix_data_type*,void*);
int mxt_string_thined_rep(struct matrix_data_type * tp,
	MATRIX *m,INTEGER64* dim_code,int * rep,int,void * dest,void * src);
void gc_mx_stringv_data(void * d);
void mxt_convert_stringv_to_net(MATRIX_DATA_TYPE *,RECORD_LIST64*,void*);
void *mxt_convert_stringv_to_host(MATRIX_DATA_TYPE*,void*,int,void*);
void
mxt_print_string(MATRIX_DATA_TYPE*tp,MATRIX_STRING_BUFFER*b,void*d,char*fmt);
void
mxt_print_string_vector(
	MATRIX_DATA_TYPE * tp,
	MATRIX_STRING_BUFFER * b,
	void * d,char*fmt);

MATRIX_DATA_TYPE mx_type_string = {
	MDT_STRING,
	0,
	mxt_string_get_size,
	mxt_string_sexp2md,
	mxt_string_md2sexp,
	mxt_string_cmp,
	mxt_vector_copy,
	mxt_alloc_copy,
	xx_mxt_alloc_data,
	mxt_free_data,
	0,
	0,
	0,
	0,
	mxt_endian_nothing,
	mxt_endian_nothing,
	mxt_convert_basic_to_net,
	mxt_convert_basic_to_host,
	0,0,0,0,0,0,0,
	mxt_print_string
};

MATRIX_DATA_TYPE mx_type_string_v = {
	MDT_STRING|MDT_VECTOR,
	&mx_type_string,
	mxt_stringv_get_size,
	mxt_vector_sexp2md,
	mxt_vector_md2sexp,
	mxt_stringv_cmp,
	mxt_stringv_copy,
	mxt_alloc_stringv_copy,
	mxt_alloc_stringv_data,
	mxt_free_stringv_data,
	mxt_string_thined_rep,
	0,0,0,
	0,0,
	mxt_convert_stringv_to_net,
	mxt_convert_stringv_to_host,
	0,0,0,0,0,0,0,
	mxt_print_string_vector
};


int
mxt_string_get_size(MATRIX_DATA_TYPE * tp,void * d)
{
char * p;
int ret;
	ret = 0;
	for ( p = (char*)d ; *p ; p ++ , ret ++ );
	return ret + 1;
}


void * 
mxt_string_sexp2md(int * cpy,struct matrix_data_type* tp,XL_SEXP * s)
{
char * ret;
	if ( get_type(s) != XLT_STRING )
		return 0;
	ret = ln_copy_str(&utf8_cm,s->string.data);
	if ( cpy )
		*cpy = 1;
	return (void*)ret;
}

XL_SEXP * 
mxt_string_md2sexp(struct matrix_data_type* tp,void * d)
{
	return get_string(l_string(&utf8_cm,d));
}

int 
mxt_string_cmp(struct matrix_data_type* tp,void* d1,void* d2)
{
	return strcmp(d1,d2);
}


int
mxt_stringv_get_size(MATRIX_DATA_TYPE * tp,void * d)
{
MATRIX_DATA_HEADER * h;
int i;
int * ix;
int sz;
	h = d;
	ix = (int*)(h+1);
	sz = 1;
	for ( i = 0 ; i < h->dim ; i ++ )
		sz *= ix[i];
	return h->offset + sz * sizeof(char*);
}



int 
mxt_stringv_cmp(struct matrix_data_type* tp,void* d1,void* d2)
{
MATRIX_DH_SET h1;
MATRIX_DH_SET h2;
int ret;
char ** p1, ** p2;
int i;
	get_matrix_dh_set(&h1,d1);
	get_matrix_dh_set(&h2,d2);
	ret = cmp_dh_set(&h1,&h2);
	if ( ret )
		return ret;
	i = h1.total_element;
	for ( p1 = h1.offset , p2 = h2.offset ; i > 0 ; i -- ) {
		ret = strcmp(*p1,*p2);
		if ( ret )
			return ret;
		p1 ++;
		p2 ++;
	}
	return 0;
}



void 
mxt_stringv_copy(struct matrix_data_type* tp,void* d1,void* d2,int atype,void * at_work)
{
MATRIX_DH_SET h2;
char ** p1, ** p2;
int i;
MATRIX_DATA_HEADER * h1;
int len;
	get_matrix_dh_set(&h2,d2);
	h1 = (MATRIX_DATA_HEADER*)d1;
	*h1 = *h2.hd;

	i = h2.total_element;
	for ( p1 = (char**)(((char*)h1) + h1->offset) , p2 = h2.offset ; i > 0 ; i -- ) {
		if ( *p2 ) {
			len = strlen(*p2);
			*p1 = atype_alloc(len+1,atype,at_work);
			strcpy(*p1,*p2);
		}
		else	*p1 = 0;
		p1 ++;
		p2 ++;
	}
}


void * 
mxt_alloc_stringv_copy(struct matrix_data_type* tp,void* d,int atype,void * at_work)
{
void * ret;
	ret = d_alloc((*tp->get_size)(tp,d));
	mxt_stringv_copy(tp,ret,d,atype,at_work);
	return ret;
}

void
gc_mx_stringv_data(void * d)
{
MATRIX_DH_SET h;
int i;
char ** q;
	if ( d == 0 )
		return;
	if ( TEST_AND_SET(d) )
		return;
	get_matrix_dh_set(&h,d);
	q = (char**)h.offset;
	for ( i = 0 ; i < h.total_element ; i ++ )
		gc_text(*q++);
}

void *
mxt_alloc_stringv_data(MATRIX_DATA_TYPE * tp,int atype,void * d,void *w,
			char * __f,int __l)
{
MATRIX_ALLOC_VECTOR_PARAM * p;
int header_size;
int offset;
int ix_size;
int total_size;
MATRIX_DATA_HEADER * h;
int * ix;
int i;
char ** q;
	p = (MATRIX_ALLOC_VECTOR_PARAM*)d;
	header_size = sizeof(MATRIX_DATA_HEADER)
			+ p->dim * sizeof(int);
	offset = header_size;
	ix_size = 1;
	for ( i = 0 ; i < p->dim ; i ++ )
		ix_size *= p->ix_size[i];
	total_size = offset + ix_size * sizeof(char*);
	switch ( atype ) {
	case MD_MALLOC:
		h = malloc(total_size);
		break;
	case MD_MMALLOC:
		h = mmalloc(total_size,gc_mx_stringv_data);
		break;
	case MD_DALLOC:
		h = xx_d_alloc(total_size,__f,__l);
		break;
	case MD_CALLOC:
		h = mxc_alloc(w,total_size);
	default:
		er_panic("mxt_alloc_vector");
	}
	h->offset = offset;
	h->type = tp->type;
	h->dim = p->dim;
	ix = (int*)(h+1);
	for ( i= 0 ; i < p->dim ; i ++ )
		ix[i] = p->ix_size[i];
	if ( p->default_data ) {
 		q = (char**)(((char*)h) + offset);
 		for ( i = 0 ; i < ix_size ; i ++ ) {
 			switch ( atype ) {
 			case MD_MALLOC:
  				*q = malloc(strlen(p->default_data)+1);
  				strcpy(*q,p->default_data);
 				break;
			case MD_MMALLOC:
  				*q = copy_mstr(p->default_data);
 				break;
			case MD_DALLOC:
 				*q = copy_str(p->default_data);
 				break;
 			}
 			q ++;
		}
	}
	return (void*)h;
}

void 
mxt_free_stringv_data(struct matrix_data_type* tp,void* d)
{
MATRIX_DH_SET dh;
char ** p;
int i;
	get_matrix_dh_set(&dh,d);
	p = (char**)dh.offset;
	for ( i = dh.total_element ; i > 0 ; i -- ) {
		d_f_ree(*p);
		p ++;
	}
	d_f_ree(d);
}



int
mxt_string_thined_rep(struct matrix_data_type * tp,
	MATRIX *m,INTEGER64* dim_code,int * rep,int parent,void * dest,void * src)
{
MATRIX_DH_SET h_src;
MATRIX_DH_SET h_dest;
INTEGER64 lev;
int i;
int _ofs;
int * ofs;
int * src_ix,* dest_ix;
int * src_inc;
char ** p1, ** p2;
int dim;
int _p;
	get_matrix_dh_set(&h_src,src);
	get_matrix_dh_set(&h_dest,dest);
	if ( h_src.hd->dim != m->p.dim )
		return -1;
	if ( h_dest.hd->dim != m->p.dim )
		return -1;
	lev = dim_code[0];
	ofs = d_alloc(sizeof(int)*m->p.dim);
	for ( i = 0 ; i < m->p.dim ; i ++ ) {
		_p = m->dim_divide[i]*parent;
		if ( m->block_size[i] > _p ) {
			_ofs = 1<<(m->block_size[i] - _p);
			ofs[i] = _ofs * ((dim_code[i+1] >> 
					(lev * m->dim_divide[i] + m->block_size[i]))
				& ((1<<m->dim_divide[i]*parent)-1));
		}
		else	ofs[i] = 0;
	}
	dim = m->p.dim;
	src_ix = d_alloc(sizeof(int)*dim);
	dest_ix = d_alloc(sizeof(int)*dim);
	src_inc = d_alloc(sizeof(int)*dim);
	memcpy(src_ix,rep,sizeof(int)*dim);
	for ( i = 0 ; i < dim ; i ++ ) {
		src_inc[i] = 1<<(m->dim_divide[i]*parent);
	}
	for ( ; ; ) {
		for (  i = 0 ; i < dim ; i ++ )
			dest_ix[i] = (src_ix[i]>>(parent*m->dim_divide[i])) + ofs[i];
		p1 = &((char**)h_src.offset)[get_seq_from_ix(src_ix,h_src.ix,dim)];
		p2 = &((char**)h_dest.offset)[get_seq_from_ix(dest_ix,h_dest.ix,dim)];
		*p2 = copy_str(*p1);
		if ( inc_ix(src_ix,rep,src_inc,h_src.ix,dim) )
			break;
	}
	d_f_ree(src_ix);
	d_f_ree(dest_ix);
	d_f_ree(src_inc);
	d_f_ree(ofs);
	return 0;
}



void 
mxt_convert_stringv_to_net(MATRIX_DATA_TYPE * tp,RECORD_LIST64* rlp,void* d)
{
int i;
MATRIX_DH_SET ds;
char ** p;
int * hd;
int size;
char ch;
	get_matrix_dh_set(&ds,d);
	p = ds.offset;
	hd = d_alloc(size=sizeof(int)*(ds.hd->dim + 1));
	hd[0] = (((int)ds.hd->type)<<16) | ds.hd->dim;
	for ( i = 0 ; i < ds.hd->dim ; i ++ )
		hd[i+1] = ds.ix[i];
	for ( i = 0; i < ds.hd->dim + 1 ; i ++ ) {
		change_endian(hd[i]);
	}
	set_recordlist_chain64(rlp,hd,size,1);
	ch = 0;
	for ( i = 0 ; i < ds.total_element ; i ++ , p ++ ) {
		if ( *p )
			set_recordlist_chain64(rlp,*p,strlen(*p)+1,0);
		else
			set_recordlist_chain64(rlp,&ch,1,0);
	}
}

void *
mxt_convert_stringv_to_host(MATRIX_DATA_TYPE* tp,void* d,int atype,void * at_work)
{
int i;
int * hd;
int dim;
int type;
int * ptr;
char * cptr;
char ** q;
int * ix;
void * ret;
MATRIX_ALLOC_VECTOR_PARAM vp;
MATRIX_DH_SET ds;
int len;
	hd = d;
	change_endian(hd[0]);
	type = (hd[0]>>16) & 0xffff;
	dim = hd[0] & 0xffff;
	ix = ptr = &hd[1];
	for ( i = 0 ; i < dim ; i ++ ) {
		change_endian((*ptr));
		ptr ++;
	}
	cptr = (char*)ptr;
	vp.dim = dim;
	vp.ix_size = ix;
	vp.default_data = 0;
	ret = (*tp->alloc_data)(tp,atype,&vp,at_work,__FILE__,__LINE__);
	get_matrix_dh_set(&ds,ret);
	q = ds.offset;
	for ( i = 0 ; i < ds.total_element ; i ++ ) {
		if ( *cptr == 0 ) {
			*q++ = 0;
			cptr ++;
		}
		else {
			len = strlen(cptr)+1;
			switch ( atype ) {
			case MD_MMALLOC:
				*q = atype_alloc(len,atype,gc_text);
				break;
			default:
				*q = atype_alloc(len,atype,at_work);
				break;
			}
			strcpy(*q,cptr);
			q++;
			cptr += len;
		}
	}
	return ret;
}



void
mxt_print_string(MATRIX_DATA_TYPE*tp,MATRIX_STRING_BUFFER*b,void*d,char*fmt)
{
	out_matrix_string_buffer(b,d);
}


void
mxt_print_string_vector(
	MATRIX_DATA_TYPE * tp,
	MATRIX_STRING_BUFFER * b,
	void * d,char*fmt)
{
char buffer[20];
MATRIX_DH_SET ds;
int i,j;
int el_size;
char * ptr;
	get_matrix_dh_set(&ds,d);
	sprintf(buffer,"[dim:%i ",ds.hd->dim);
	out_matrix_string_buffer(b,buffer);

	for ( i = 0 ; i < ds.hd->dim ; i ++ ) {
		sprintf(buffer,"%i ",ds.ix[i]);
		out_matrix_string_buffer(b,buffer);
	}
	sprintf(buffer,":::\n");
	out_matrix_string_buffer(b,buffer);

	el_size = sizeof(char*);
	ptr = ds.offset;
	switch ( ds.hd->dim ) {
	case 1:
		for ( j = 0 ; j < ds.total_element ; j ++ ) {
			(*tp->parent->print_data)(
				tp->parent,
				b,
				ptr,
				fmt);
			ptr += el_size;
		}
		break;
	default:
		for ( j = 0 ; j < ds.total_element ; j ++ ) {
			(*tp->parent->print_data)(
				tp->parent,
				b,
				ptr,
				fmt);
			ptr += el_size;
			if ( ((j+1) % ds.ix[0]) == 0 && 
					(j+1) != ds.total_element )
				out_matrix_string_buffer(b," /\n");
			else	out_matrix_string_buffer(b," ");
		}
		break;
	}
	out_matrix_string_buffer(b,"]");
}




