/**********************************************************************
 
	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"

#define BASE_TYPE 	int

int mxt_RGB_get_size(MATRIX_DATA_TYPE *,void * d);
void * mxt_RGB_sexp2md(int * cpy,struct matrix_data_type*,XL_SEXP * s);
XL_SEXP * mxt_RGB_md2sexp(struct matrix_data_type*,void * d);
int mxt_RGB_cmp(struct matrix_data_type*,void*,void*);
int mxt_RGBv_cmp(struct matrix_data_type*,void*,void*);
int cmp_RGB(int r1,int r2);
int mxt_RGB_thined_rep(struct matrix_data_type * tp,
	MATRIX *m,INTEGER64* dim_code,int * rep,int,void * dest,void * src);
int mxt_RGB_thined_avg(struct matrix_data_type * tp,
	MATRIX *m,INTEGER64* dim_code,int,void * dest,void * src);
int add_RGB(int a,int b,int);
int sub_RGB(int a,int b,int);
int mxt_RGB_add(struct matrix_data_type * tp,
	MATRIX *m,INTEGER64* dim_code,int parent,void * opt,void * dest,void * src1,void * src2);
int mxt_RGB_sub(struct matrix_data_type * tp,
	MATRIX *m,INTEGER64* dim_code,int parent,void * opt,void * dest,void * src1,void * src2);
void mxt_endian_int32(void*);

MATRIX_DATA_TYPE mx_type_RGB = {
	MDT_RGB8,
	0,
	mxt_RGB_get_size,
	mxt_RGB_sexp2md,
	mxt_RGB_md2sexp,
	mxt_RGB_cmp,
	mxt_vector_copy,
	mxt_alloc_copy,
	xx_mxt_alloc_data,
	mxt_free_data,
	0,
	0,
	0,
	0,
	mxt_endian_int32,
	mxt_endian_int32,
	mxt_convert_basic_to_net,
	mxt_convert_basic_to_host
};

MATRIX_DATA_TYPE mx_type_RGB_v = {
	MDT_RGB8|MDT_VECTOR,
	&mx_type_RGB,
	mxt_vector_get_size,
	mxt_vector_sexp2md,
	mxt_vector_md2sexp,
	mxt_RGBv_cmp,
	mxt_vector_copy,
	mxt_alloc_copy,
	xx_mxt_alloc_vector,
	mxt_free_data,
	mxt_RGB_thined_rep,
	mxt_RGB_thined_avg,
	mxt_RGB_add,
	mxt_RGB_sub,
	mxt_endian_vector_to_net,
	mxt_endian_vector_to_host,
	mxt_convert_basic_to_net,
	mxt_convert_basic_to_host
};

int
cmp_RGB(int r1,int r2)
{
int i;
	for ( i = 0 ; i < 4 ; i ++ ) {
		if ( (r1 & 0x0ff) < (r2 & 0x0ff) )
			return -1;
		if ( (r1 & 0x0ff) > (r2 & 0x0ff) )
			return 1;
		r1 = r1 >> 8;
		r2 = r2 >> 8;
	}
	return 0;
}


int
mxt_RGB_get_size(MATRIX_DATA_TYPE * tp,void * d)
{
	return sizeof(BASE_TYPE);
}


void * 
mxt_RGB_sexp2md(int * cpy,struct matrix_data_type* tp,XL_SEXP * s)
{
BASE_TYPE * ret;
	if ( get_type(s) != XLT_INTEGER )
		return 0;
	ret = d_alloc(sizeof(BASE_TYPE));
	*ret = s->integer.data;
	if ( cpy )
		*cpy = 1;
	return (void*)ret;
}

XL_SEXP * 
mxt_RGB_md2sexp(struct matrix_data_type* tp,void * d)
{
BASE_TYPE * _d;
	_d = d;
	return get_integer(*_d,0);
}

int 
mxt_RGB_cmp(struct matrix_data_type* tp,void* d1,void* d2)
{
BASE_TYPE * _d1, * _d2;
	_d1 = (BASE_TYPE*)d1;
	_d2 = (BASE_TYPE*)d2;
	return cmp_RGB(*_d1,*_d2);
}



int 
mxt_RGBv_cmp(struct matrix_data_type* tp,void* d1,void* d2)
{
MATRIX_DH_SET h1;
MATRIX_DH_SET h2;
int ret;
BASE_TYPE * 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 = cmp_RGB(*p1,*p2);
		if ( ret )
			return ret;
		p1 ++;
		p2 ++;
	}
	return 0;
}



int
mxt_RGB_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;
BASE_TYPE * 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];
	if ( lev <= 0 )
		return -1;
	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));
		}
	}
	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 = &((BASE_TYPE*)h_src.offset)[get_seq_from_ix(src_ix,h_src.ix,dim)];
		p2 = &((BASE_TYPE*)h_dest.offset)[get_seq_from_ix(dest_ix,h_dest.ix,dim)];
		*p2 = *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;
}




int
mxt_RGB_thined_avg(struct matrix_data_type * tp,
	MATRIX *m,INTEGER64* dim_code,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, * avg_ix,* avg_size;
int * src_inc,* dest_inc;
BASE_TYPE * p1, * p2;
int dim;
int avg;
int n_size;
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];
	if ( lev <= 0 )
		return -1;
	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));
		}
	}
	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);
	dest_inc = d_alloc(sizeof(int)*dim);
	avg_ix = d_alloc(sizeof(int)*dim);
	avg_size = d_alloc(sizeof(int)*dim);
	n_size = 1;
	for ( i = 0 ; i < dim ; i ++ ) {
		src_ix[i] = 0;
		src_inc[i] = 1<<(m->dim_divide[i]*parent);
		n_size *= src_inc[i];
		dest_inc[i] = 1;
		dest_ix[i] = ofs[i];
	}
	for ( ; ; ) {
		memcpy(avg_ix,src_ix,sizeof(int)*dim);
		avg = 0;
		for ( i = 0 ; i < dim ; i ++ ) {
			avg_size[i] = src_ix[i] + src_inc[i];
			dest_ix[i] = src_ix[i]>>(m->dim_divide[i]*parent);
		}
		for ( ; ; ) {
			p1 = &((BASE_TYPE*)h_src.offset)[get_seq_from_ix(avg_ix,h_src.ix,dim)];
			avg += *p1;
			if ( inc_ix(avg_ix,src_ix,dest_inc,avg_size,dim) )
				break;
		}
		p2 = &((BASE_TYPE*)h_dest.offset)[get_seq_from_ix(dest_ix,h_dest.ix,dim)];
		*p2 = avg/n_size;
		if ( inc_ix(src_ix,0,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(dest_inc);
	d_f_ree(ofs);
	d_f_ree(avg_ix);
	d_f_ree(avg_size);
	return 0;
}


int
add_RGB(int a,int b,int opt)
{
int i;
int c1,c2,c3;
int ret;
	if ( opt == OPT_CURVE ) {
		ret = 0;
		for ( i = 0 ; i < 4 ; i ++ ) {
			c1 = (a >> (8*i)) & 0x0ff;
			c2 = (b >> (8*i)) & 0x0ff;
			c3 = c1 + __uchar2short(c2);
			if ( c3 >= 256 )
				c3 = 255;
			ret += c3 << (8*i);
		}
	}
	else {
		ret = 0;
		for ( i = 0 ; i < 4 ; i ++ ) {
			c1 = (a >> (8*i)) & 0x0ff;
			c2 = (b >> (8*i)) & 0x0ff;
			c3 = c1 + c2;
			if ( c3 >= 256 )
				c3 = 255;
			ret += c3 << (8*i);
		}
	}
	return ret;
}


int
sub_RGB(int a,int b,int opt)
{
int i;
int c1,c2,c3;
int ret;
	if ( opt == OPT_CURVE ) {
		ret = 0;
		for ( i = 0 ; i < 4 ; i ++ ) {
			c1 = (a >> (8*i)) & 0x0ff;
			c2 = (b >> (8*i)) & 0x0ff;
			c3 = __short2uchar(c1 - c2);
			ret += c3 << (8*i);
		}
	}
	else {
		ret = 0;
		for ( i = 0 ; i < 4 ; i ++ ) {
			c1 = (a >> (8*i)) & 0x0ff;
			c2 = (b >> (8*i)) & 0x0ff;
			c3 = c1 - c2;
			if ( c3 >= 256 )
				c3 = 255;
			if ( c3 < 0 )
				c3 = 0;
			ret += c3 << (8*i);
		}
	}
	return ret;
}



int
mxt_RGB_add(struct matrix_data_type * tp,
	MATRIX *m,INTEGER64* dim_code,int parent,void * opt,void * dest,void * src1,void * src2)
{
MATRIX_DH_SET h_src1,h_src2;
MATRIX_DH_SET h_dest;
INTEGER64 lev;
int i;
int _ofs;
int * ofs;
int * src1_ix, * src2_ix;
int * src1_inc,* src2_inc;
int * target_ix,* target_size;
BASE_TYPE * p1, * p2, * dest_p;
int dim;
int _p;
int * _opt;
	_opt = (int*)opt;
	get_matrix_dh_set(&h_src1,src1);
	get_matrix_dh_set(&h_src2,src2);
	get_matrix_dh_set(&h_dest,dest);
	if ( h_src1.hd->dim != m->p.dim )
		return -1;
	if ( h_src2.hd->dim != m->p.dim )
		return -1;
	if ( h_dest.hd->dim != m->p.dim )
		return -1;
	lev = dim_code[0];
	if ( lev <= 0 )
		return -1;
	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));
		}
	}
	dim = m->p.dim;
	src1_ix = d_alloc(sizeof(int)*dim);
	src2_ix = d_alloc(sizeof(int)*dim);
	src1_inc = d_alloc(sizeof(int)*dim);
	src2_inc = d_alloc(sizeof(int)*dim);
	target_ix = d_alloc(sizeof(int)*dim);
	target_size = d_alloc(sizeof(int)*dim);
	for ( i = 0 ; i < dim ; i ++ ) {
		src2_ix[i] = 0;
		src2_inc[i] = 1<<(m->dim_divide[i]*parent);
		src1_inc[i] = 1;
		src1_ix[i] = ofs[i];
	}
	for ( ; ; ) {
		p1 = &((BASE_TYPE*)h_src1.offset)[get_seq_from_ix(src1_ix,h_src1.ix,dim)];

		memcpy(target_ix,src2_ix,sizeof(int)*dim);
		for ( i = 0 ; i < dim ; i ++ )
			target_size[i] = src2_ix[i] + src2_inc[i];
		for ( ; ; ) {
			p2 = &((BASE_TYPE*)h_src2.offset)
				[get_seq_from_ix(target_ix,h_src2.ix,dim)];
			dest_p = &((BASE_TYPE*)h_dest.offset)
				[get_seq_from_ix(target_ix,h_dest.ix,dim)];
			(*dest_p) = add_RGB((*p1),(*p2),*_opt);
			if ( inc_ix(target_ix,src2_ix,src1_inc,target_size,dim) )
				break;
		}
		if ( inc_ix(src2_ix,0,src2_inc,h_src2.ix,dim) )
			break;
		inc_ix(src1_ix,ofs,src1_inc,h_src1.ix,dim);
	}
	d_f_ree(src1_ix);
	d_f_ree(src2_ix);
	d_f_ree(src1_inc);
	d_f_ree(src2_inc);
	d_f_ree(ofs);
	d_f_ree(target_ix);
	d_f_ree(target_size);
	return 0;
}



int
mxt_RGB_sub(struct matrix_data_type * tp,
	MATRIX *m,INTEGER64* dim_code,int parent,void * opt,void * dest,void * src1,void * src2)
{
MATRIX_DH_SET h_src1,h_src2;
MATRIX_DH_SET h_dest;
INTEGER64 lev;
int i;
int _ofs;
int * ofs;
int * src1_ix, * src2_ix;
int * src1_inc,* src2_inc;
int * target_ix,* target_size;
BASE_TYPE * p1, * p2, * dest_p;
int dim;
int _p;
int *_opt;
	_opt = (int*)opt;
	get_matrix_dh_set(&h_src1,src1);
	get_matrix_dh_set(&h_src2,src2);
	get_matrix_dh_set(&h_dest,dest);
	if ( h_src1.hd->dim != m->p.dim )
		return -1;
	if ( h_src2.hd->dim != m->p.dim )
		return -1;
	if ( h_dest.hd->dim != m->p.dim )
		return -1;
	lev = dim_code[0];
	if ( lev <= 0 )
		return -1;
	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));
		}
	}
	dim = m->p.dim;
	src1_ix = d_alloc(sizeof(int)*dim);
	src2_ix = d_alloc(sizeof(int)*dim);
	src1_inc = d_alloc(sizeof(int)*dim);
	src2_inc = d_alloc(sizeof(int)*dim);
	target_ix = d_alloc(sizeof(int)*dim);
	target_size = d_alloc(sizeof(int)*dim);
	for ( i = 0 ; i < dim ; i ++ ) {
		src2_ix[i] = 0;
		src2_inc[i] = 1<<(m->dim_divide[i]*parent);
		src1_inc[i] = 1;
		src1_ix[i] = ofs[i];
	}
	for ( ; ; ) {
		p1 = &((BASE_TYPE*)h_src1.offset)[get_seq_from_ix(src1_ix,h_src1.ix,dim)];

		memcpy(target_ix,src2_ix,sizeof(int)*dim);
		for ( i = 0 ; i < dim ; i ++ )
			target_size[i] = src2_ix[i] + src2_inc[i];
		for ( ; ; ) {
			p2 = &((BASE_TYPE*)h_src2.offset)
				[get_seq_from_ix(target_ix,h_src2.ix,dim)];
			dest_p = &((BASE_TYPE*)h_dest.offset)
				[get_seq_from_ix(target_ix,h_dest.ix,dim)];
			(*dest_p) = sub_RGB((*p1),(*p2),*_opt);
			if ( inc_ix(target_ix,src2_ix,src1_inc,target_size,dim) )
				break;
		}
		if ( inc_ix(src2_ix,0,src2_inc,h_src2.ix,dim) )
			break;
		inc_ix(src1_ix,ofs,src1_inc,h_src1.ix,dim);
	}
	d_f_ree(src1_ix);
	d_f_ree(src2_ix);
	d_f_ree(src1_inc);
	d_f_ree(src2_inc);
	d_f_ree(ofs);
	d_f_ree(target_ix);
	d_f_ree(target_size);
	return 0;
}




